From 67f91a09df1ec5d7924538a89cdd9adc662a2ed3 Mon Sep 17 00:00:00 2001 From: stephengold Date: Thu, 19 Sep 2024 14:30:40 -0700 Subject: [PATCH] rework BodyVector and add the BodyManager class --- .../stephengold/joltjni/BodyManager.java | 208 ++++++++++++++++++ .../stephengold/joltjni/BodyVector.java | 49 +++-- src/main/native/glue/b/BodyManager.cpp | 188 ++++++++++++++++ src/main/native/glue/b/BodyVector.cpp | 24 -- 4 files changed, 427 insertions(+), 42 deletions(-) create mode 100644 src/main/java/com/github/stephengold/joltjni/BodyManager.java create mode 100644 src/main/native/glue/b/BodyManager.cpp diff --git a/src/main/java/com/github/stephengold/joltjni/BodyManager.java b/src/main/java/com/github/stephengold/joltjni/BodyManager.java new file mode 100644 index 00000000..59f8f52d --- /dev/null +++ b/src/main/java/com/github/stephengold/joltjni/BodyManager.java @@ -0,0 +1,208 @@ +/* +Copyright (c) 2024 Stephen Gold + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ +package com.github.stephengold.joltjni; + +import com.github.stephengold.joltjni.readonly.ConstBodyCreationSettings; +import com.github.stephengold.joltjni.readonly.ConstBodyId; + +/** + * A container for bodies. + * + * @author Stephen Gold sgold@sonic.net + */ +public class BodyManager extends NonCopyable { + // ************************************************************************* + // constructors + + /** + * Instantiate a default manager. + */ + public BodyManager() { + long managerVa = createBodyManager(); + setVirtualAddress(managerVa, true); + } + // ************************************************************************* + // new methods exposed + + /** + * Activate the specified bodies. + * + * @param idArray the IDs of the bodies to activate (not null, unaffected) + */ + public void activateBodies(ConstBodyId... idArray) { + activateBodies(idArray, idArray.length); + } + + /** + * Activate the specified bodies. + * + * @param idArray the IDs of the bodies to activate (not null, unaffected) + * @param number the number of IDs in the array (≥0) + */ + public void activateBodies(ConstBodyId[] idArray, int number) { + long managerVa = va(); + long[] idVas = new long[number]; + for (int i = 0; i < number; ++i) { + idVas[i] = idArray[i].va(); + } + activateBodies(managerVa, idVas); + } + + /** + * Add a body, assigning the next available ID. + * + * @param body (not null, modified) + * @return true if successful, false if no ID is available + */ + public boolean addBody(Body body) { + long managerVa = va(); + long bodyVa = body.va(); + boolean result = addBody(managerVa, bodyVa); + + return result; + } + + /** + * Create a body using the specified settings, but do not add it. + * + * @param settings the settings to use (not null, unaffected) + * @return a new body, not added to any manager + */ + public Body allocateBody(ConstBodyCreationSettings settings) { + long managerVa = va(); + long settingsVa = settings.va(); + long bodyVa = allocateBody(managerVa, settingsVa); + Body result = new Body(bodyVa); + + return result; + } + + /** + * Deactivate the specified bodies. + * + * @param idArray the IDs of the bodies to deactivate (not null, unaffected) + */ + public void deactivateBodies(ConstBodyId... idArray) { + deactivateBodies(idArray, idArray.length); + } + + /** + * Deactivate the specified bodies. + * + * @param idArray the IDs of the bodies to deactivate (not null, unaffected) + * @param number the number of IDs in the array (≥0) + */ + public void deactivateBodies(ConstBodyId[] idArray, int number) { + long managerVa = va(); + long[] idVas = new long[number]; + for (int i = 0; i < number; ++i) { + idVas[i] = idArray[i].va(); + } + deactivateBodies(managerVa, idVas); + } + + /** + * Remove the specified bodies from the manager. + * + * @param idArray the IDs of the bodies to destroy (not null, unaffected) + */ + public void destroyBodies(ConstBodyId... idArray) { + destroyBodies(idArray, idArray.length); + } + + /** + * Remove the specified bodies from the manager. + * + * @param idArray the IDs of the bodies to destroy (not null, unaffected) + * @param number the number of IDs in the array (≥0) + */ + public void destroyBodies(ConstBodyId[] idArray, int number) { + long managerVa = va(); + long[] idVas = new long[number]; + for (int i = 0; i < number; ++i) { + idVas[i] = idArray[i].va(); + } + destroyBodies(managerVa, idVas); + } + + /** + * Draw the state of the bodies. + * + * @param drawSettings the draw settings to use (not null, unaffected) + * @param physicsSettings the physics settings to use (not null, unaffected) + * @param renderer the renderer to use (not null) + */ + public void draw(BodyManagerDrawSettings drawSettings, + PhysicsSettings physicsSettings, DebugRenderer renderer) { + long managerVa = va(); + long drawSettingsVa = drawSettings.va(); + long physicsSettingsVa = physicsSettings.va(); + long rendererVa = renderer.va(); + draw(managerVa, drawSettingsVa, physicsSettingsVa, rendererVa); + } + + /** + * Return all bodies. + * + * @return a new vector (may contain invalid body pointers) + */ + public BodyVector getBodies() { + long managerVa = va(); + long vectorVa = getBodies(managerVa); + BodyVector result = new BodyVector(this, vectorVa); + + return result; + } + + /** + * Return the maximum number of bodies the manager can support. + * + * @return the count (≥0) + */ + public int getMaxBodies() { + long managerVa = va(); + int result = getMaxBodies(managerVa); + + return result; + } + // ************************************************************************* + // native private methods + + native private static void activateBodies(long managerVa, long[] idVas); + + native private static boolean addBody(long managerVa, long bodyVa); + + native private static long allocateBody(long managerVa, long settingsVa); + + native private static long createBodyManager(); + + native private static void deactivateBodies(long managerVa, long[] idVas); + + native private static void destroyBodies(long managerVa, long[] idVas); + + native private static void draw(long managerVa, long drawSettingsVa, + long physicsSettingsVa, long rendererVa); + + native private static long getBodies(long managerVa); + + native private static int getMaxBodies(long managerVa); +} diff --git a/src/main/java/com/github/stephengold/joltjni/BodyVector.java b/src/main/java/com/github/stephengold/joltjni/BodyVector.java index ee13590e..fc920ea0 100644 --- a/src/main/java/com/github/stephengold/joltjni/BodyVector.java +++ b/src/main/java/com/github/stephengold/joltjni/BodyVector.java @@ -31,27 +31,38 @@ of this software and associated documentation files (the "Software"), to deal */ public class BodyVector extends Array { // ************************************************************************* - // constructors + // fields /** - * Instantiate an empty vector. + * prevent premature garbage collection of the underlying + * {@code BodyManager} */ - public BodyVector() { - long vectorVa = createBodyVector(); - setVirtualAddress(vectorVa, () -> free(vectorVa)); - } + final private BodyManager manager; + // ************************************************************************* + // constructors /** - * Instantiate a vector with the specified native object assigned. + * Instantiate a vector with the specified native object assigned but not + * owned. * + * @param manager the underlying {@code BodyManager} (not null) * @param vectorVa the virtual address of the native object to assign (not * zero) - * @param owner true → make the current object the owner, false → - * the current object isn't the owner */ - BodyVector(long vectorVa, boolean owner) { - Runnable freeingAction = owner ? () -> free(vectorVa) : null; - setVirtualAddress(vectorVa, freeingAction); + BodyVector(BodyManager manager, long vectorVa) { + this.manager = manager; + setVirtualAddress(vectorVa, null); + } + // ************************************************************************* + // new methods exposed + + /** + * Access the underlying {@code BodyManager}. + * + * @return the pre-existing instance (not null) + */ + public BodyManager getManager() { + return manager; } // ************************************************************************* // Array methods @@ -74,13 +85,19 @@ public int capacity() { * Access the body at the specified index. * * @param elementIndex the index from which to get the body (≥0) - * @return a new JVM object with the pre-existing native object assigned + * @return a new JVM object with the pre-existing native object assigned, or + * else {@code null} */ @Override public Body get(int elementIndex) { long vectorVa = va(); long bodyVa = getBody(vectorVa, elementIndex); - Body result = new Body(bodyVa); + Body result; + if (bodyVa == 0L) { + result = null; + } else { + result = new Body(bodyVa); + } return result; } @@ -126,10 +143,6 @@ public int size() { native private static int capacity(long vectorVa); - native private static long createBodyVector(); - - native private static void free(long vectorVa); - native private static long getBody(long vectorVa, int elementIndex); native private static void resize(long vectorVa, int numBodies); diff --git a/src/main/native/glue/b/BodyManager.cpp b/src/main/native/glue/b/BodyManager.cpp new file mode 100644 index 00000000..df5e22fe --- /dev/null +++ b/src/main/native/glue/b/BodyManager.cpp @@ -0,0 +1,188 @@ +/* +Copyright (c) 2024 Stephen Gold + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/* + * Author: Stephen Gold + */ +#include +#include +#include "auto/com_github_stephengold_joltjni_BodyManager.h" +#include "glue/glue.h" + +using namespace JPH; + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: activateBodies + * Signature: (J[J)V + */ +JNIEXPORT void JNICALL Java_com_github_stephengold_joltjni_BodyManager_activateBodies + (JNIEnv *pEnv, jclass, jlong managerVa, jlongArray idVas) { + BodyManager * const pManager = reinterpret_cast (managerVa); + const jsize numBodies = pEnv->GetArrayLength(idVas); + BodyID * const pTempArray = new BodyID[numBodies]; + TRACE_NEW("BodyID[]", pTempArray) + jboolean isCopy; + jlong * const pIdVas = pEnv->GetLongArrayElements(idVas, &isCopy); + for (int i = 0; i < numBodies; ++i) { + const jlong idVa = pIdVas[i]; + BodyID * const pId = reinterpret_cast (idVa); + pTempArray[i] = *pId; + } + pEnv->ReleaseLongArrayElements(idVas, pIdVas, JNI_ABORT); + pManager->ActivateBodies(pTempArray, numBodies); + TRACE_DELETE("BodyID[]", pTempArray) + delete[] pTempArray; +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: addBody + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_github_stephengold_joltjni_BodyManager_addBody + (JNIEnv *, jclass, jlong managerVa, jlong bodyVa) { + BodyManager * const pManager = reinterpret_cast (managerVa); + Body * const pBody = reinterpret_cast (bodyVa); + bool result = pManager->AddBody(pBody); + return result; +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: allocateBody + * Signature: (JJ)J + */ +JNIEXPORT jlong JNICALL Java_com_github_stephengold_joltjni_BodyManager_allocateBody + (JNIEnv *, jclass, jlong managerVa, jlong settingsVa) { + BodyManager * const pManager = reinterpret_cast (managerVa); + const BodyCreationSettings * const pSettings + = reinterpret_cast (settingsVa); + Body * pResult = pManager->AllocateBody(*pSettings); + return reinterpret_cast (pResult); +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: createBodyManager + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_com_github_stephengold_joltjni_BodyManager_createBodyManager + (JNIEnv *, jclass) { + BodyManager * const pManager = new BodyManager(); + TRACE_NEW("BodyManager", pManager) + return reinterpret_cast (pManager); +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: deactivateBodies + * Signature: (J[J)V + */ +JNIEXPORT void JNICALL Java_com_github_stephengold_joltjni_BodyManager_deactivateBodies + (JNIEnv *pEnv, jclass, jlong managerVa, jlongArray idVas) { + BodyManager * const pManager = reinterpret_cast (managerVa); + const jsize numBodies = pEnv->GetArrayLength(idVas); + BodyID * const pTempArray = new BodyID[numBodies]; + TRACE_NEW("BodyID[]", pTempArray) + jboolean isCopy; + jlong * const pIdVas = pEnv->GetLongArrayElements(idVas, &isCopy); + for (int i = 0; i < numBodies; ++i) { + const jlong idVa = pIdVas[i]; + BodyID * const pId = reinterpret_cast (idVa); + pTempArray[i] = *pId; + } + pEnv->ReleaseLongArrayElements(idVas, pIdVas, JNI_ABORT); + pManager->DeactivateBodies(pTempArray, numBodies); + TRACE_DELETE("BodyID[]", pTempArray) + delete[] pTempArray; +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: destroyBodies + * Signature: (J[J)V + */ +JNIEXPORT void JNICALL Java_com_github_stephengold_joltjni_BodyManager_destroyBodies + (JNIEnv *pEnv, jclass, jlong managerVa, jlongArray idVas) { + BodyManager * const pManager = reinterpret_cast (managerVa); + const jsize numBodies = pEnv->GetArrayLength(idVas); + BodyID * const pTempArray = new BodyID[numBodies]; + TRACE_NEW("BodyID[]", pTempArray) + jboolean isCopy; + jlong * const pIdVas = pEnv->GetLongArrayElements(idVas, &isCopy); + for (int i = 0; i < numBodies; ++i) { + const jlong idVa = pIdVas[i]; + BodyID * const pId = reinterpret_cast (idVa); + pTempArray[i] = *pId; + } + pEnv->ReleaseLongArrayElements(idVas, pIdVas, JNI_ABORT); + pManager->DestroyBodies(pTempArray, numBodies); + TRACE_DELETE("BodyID[]", pTempArray) + delete[] pTempArray; +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: draw + * Signature: (JJJJ)V + */ +JNIEXPORT void JNICALL Java_com_github_stephengold_joltjni_BodyManager_draw + (JNIEnv *, jclass, jlong managerVa, jlong drawSettingsVa, + jlong physicsSettingsVa, jlong rendererVa) { +#ifdef JPH_DEBUG_RENDERER + BodyManager * const pManager + = reinterpret_cast (managerVa); + const BodyManager::DrawSettings * const pDrawSettings + = reinterpret_cast (drawSettingsVa); + const PhysicsSettings * const pPhysicsSettings + = reinterpret_cast (physicsSettingsVa); + DebugRenderer * const pRenderer + = reinterpret_cast (rendererVa); + pManager->Draw(*pDrawSettings, *pPhysicsSettings, pRenderer); +#endif +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: getBodies + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_github_stephengold_joltjni_BodyManager_getBodies + (JNIEnv *, jclass, jlong managerVa) { + BodyManager * const pManager = reinterpret_cast (managerVa); + const BodyVector * const pResult = &pManager->GetBodies(); + return reinterpret_cast (pResult); +} + +/* + * Class: com_github_stephengold_joltjni_BodyManager + * Method: getMaxBodies + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_github_stephengold_joltjni_BodyManager_getMaxBodies + (JNIEnv *, jclass, jlong managerVa) { + const BodyManager * const pManager + = reinterpret_cast (managerVa); + const uint result = pManager->GetMaxBodies(); + return result; +} \ No newline at end of file diff --git a/src/main/native/glue/b/BodyVector.cpp b/src/main/native/glue/b/BodyVector.cpp index ec0c963c..823445eb 100644 --- a/src/main/native/glue/b/BodyVector.cpp +++ b/src/main/native/glue/b/BodyVector.cpp @@ -43,30 +43,6 @@ JNIEXPORT jint JNICALL Java_com_github_stephengold_joltjni_BodyVector_capacity return result; } -/* - * Class: com_github_stephengold_joltjni_BodyVector - * Method: createBodyVector - * Signature: ()J - */ -JNIEXPORT jlong JNICALL Java_com_github_stephengold_joltjni_BodyVector_createBodyVector - (JNIEnv *, jclass) { - BodyVector * const pVector = new BodyVector(); - TRACE_NEW("BodyVector", pVector) - return reinterpret_cast (pVector); -} - -/* - * Class: com_github_stephengold_joltjni_BodyVector - * Method: free - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_com_github_stephengold_joltjni_BodyVector_free - (JNIEnv *, jclass, jlong vectorVa) { - BodyVector * const pVector = reinterpret_cast (vectorVa); - TRACE_DELETE("BodyVector", pVector) - delete pVector; -} - /* * Class: com_github_stephengold_joltjni_BodyVector * Method: getBody