Compare commits

...

540 Commits

Author SHA1 Message Date
Stephen Gold 6402aad452 InstancedNode: protect the no-arg constructor and javadoc it 5 years ago
Trevor Flynn e78f303a9d
Add ability to remove morph targets from mesh (#1378) 5 years ago
kkolyan dd0c169d62 avoid NegativeArraySizeException if input string is "\\#FFF#" 5 years ago
René Kraneis 32e8b68ea0
Adding getters/setters to AnimComposer (#1376) 5 years ago
kkolyan eb7aab9704 Camera#contains javadoc updated 5 years ago
pspeed42 d29d8f5100 Fixing the angle ordering in the fromAngles() javadoc. 5 years ago
Stephen Gold acc5cfa995 AppSettings: correct a typo in the javadoc 5 years ago
Stephen Gold f34e660f03 AppSettings: change the "VSync" default from false to true 5 years ago
kkolyan 0f06c14c28 OBJLoader enhancement: named groups support 5 years ago
Stephen Gold 6ade1a027e AppSettings: change the gamma-correction default from false to true 5 years ago
Davis Rollman 30bddef8c0 Replaced Netbeans auto license with real license 5 years ago
ItsMike54 f808187142
Add Proper License to com.jme3.anim files (#1359) 5 years ago
Stephen Gold d6b0069407
restore the JOGL/JOAL hooks (#1368) 5 years ago
Stephen Gold c0692df207 Revert "Fixes issue #1336: AWTPanels forcing OpenGL 2.0 (Compat Profile)" 5 years ago
Github Actions 036b08a385 [skip ci] update natives snapshot 5 years ago
Stephen Gold 407ebc9d5e gradle.properties: trivial delta to test the recent fix for issue #1365 5 years ago
Github Actions f7352ffcfe [skip ci] update natives snapshot 5 years ago
Stephen Gold 599a0d5c56 possible fix for issue #1365 5 years ago
Stephen Gold 8aa50e9b48 Fix issue #1283 (collision-group filter not applied to CCD) 5 years ago
Stephen Gold 5ae9285903 add a test for issue #1283 5 years ago
Stephen Gold bb6ac612ff make TestMorph (in jme3-examples) more user-friendly 5 years ago
Stephen Gold 9751e23cd7 Fix issue 1360: ArmatureDebugAppState causes OpenGLException 5 years ago
Stephen Gold ceea055cc4 T key had to be pressed 2x before worked in jme3test.terrain (4 files) 5 years ago
Stephen Gold d636049a6f improve initial camera orientations in jme3test.terrain (9 files) 5 years ago
Stephen Gold 917509a04e remove an unused empty file jme3test/light/DlsfError.java 5 years ago
rvandoosselaer 3fa85bc139 Set the minimum required OpenGL profiles for the geometry and tessellation shader test cases. Resolves #1357 5 years ago
jayfella f3d81d11a1 Merge remote-tracking branch 'origin/master' 5 years ago
jayfella 37f1e7fabe Add methods to modify each channel (RGBA) that return 'this'. 5 years ago
jayfella a057a6d74d Add method to convert RGBA255 colors to ColorRGBA 5 years ago
Stephen Gold 15e3acbe4d Fix issue #1353 (TestUseAfterFree causes AssertionError) 5 years ago
Stephen Gold e3161f4622 remove an unused empty file jme3test/collision/Main.java 5 years ago
Stephen Gold c9357b4db1 Fixes issue #1350 for getters (MorphTrack documentation) 5 years ago
Stephen Gold 2e56e1f24c Fixes issue #1350 (MorphTrack documentation) 5 years ago
Stephen Gold 32f71397c5 convert tabs to blanks in FixedScale100.frag 5 years ago
Stephen Gold f6c467b495 improve javadoc in Triangle: "center" -> "centroid", "point" -> "vertex" 5 years ago
Stephen Gold 84c88709b5 correct a typographic error in TestUseAfterFree output 5 years ago
pspeed42 221acaadfe Removing the jme3-blender subproject and related references since it now lives in its 5 years ago
pspeed42 94451c4d35 Moving all of the blender-related stuff under the jme3-blender subdirectory 5 years ago
Stephen Gold 7212f9b2b2 improve format and rm trailing whitespace (10 files in com.jme3.opencl) 5 years ago
Stephen Gold a5e0213b01 add some more missing javadoc (11 files in com.jme3.math) 5 years ago
Stephen Gold a1dcbf9b06 correct javadoc errors in Matrix3f and Quaternion 5 years ago
Stephen Gold a42103651c correct the javadoc of Matrix4f.set(float[][]) -- missing "not"! 5 years ago
Stephen Gold cd7d497c79 clarify the javadoc of Quaternion.toString() "displaced"? 5 years ago
Stephen Gold 1e7c95cbb8 correct the javadoc of toString() methods (5 files in com.jme3.math) 5 years ago
Stephen Gold ee910859ac javadoc corrections: Vector4f differs from Vector3f, has 4 components! 5 years ago
Stephen Gold 5a2499ccbe improve formatting & rm trailing whitespace (21 files in com.jme3.math) 5 years ago
Stephen Gold 4c62d5008e add missing javadoc tags to BufferUtils 5 years ago
Stephen Gold 4d81fc6035 add some missing javadoc (2 files in com.jme3.util) 5 years ago
Stephen Gold fa62959451 Fixes issue #1336: AWTPanels forcing OpenGL 2.0 (Compat Profile) 5 years ago
Stephen Gold 607e068f76 FastMathTest: test the reversibility of coordinate transformations 5 years ago
Stephen Gold bb7d2cab01 add missing javadoc tags to 10 files in the com.jme3.math package 5 years ago
Stephen Gold 6cbf62a3b5 resolve an unknown HTML tag in the ColorRGBA javadoc 5 years ago
Stephen Gold 03673ac1f7 add some missing javadoc (13 files in com.jme3.math) 5 years ago
Stephen Gold 32ab16e026 javadoc correction: Quaternion.negate() doesn't "invert" anything 5 years ago
Stephen Gold 84836c6260 don't refer to transforms as "matrices" in the Transform javadoc 5 years ago
Stephen Gold 851793cde6 improve formatting & rm trailing whitespace (5 files in com.jme3.scene) 5 years ago
Stephen Gold bee3d36f16 remove the javadoc reference to the mythical USE_FAST_TRIG setting 5 years ago
Stephen Gold 50dc8349a1 remove all javadoc references to the mythical JmeException 5 years ago
Stephen Gold e99c8a74a3 improve formatting and rm trailing whitespace (16 files in jme3-core) 5 years ago
Github Actions f46a6fac11 [skip ci] update natives snapshot 5 years ago
Stephen Gold 64bfe42878 build with -Xlink:unchecked 5 years ago
Stephen Gold 1c3ed5122f suppress still more javac warnings about unchecked casts 5 years ago
Stephen Gold 70ae48af1e avoid using raw types 5 years ago
Stephen Gold c7a734d590 Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine 5 years ago
Stephen Gold 822bcd1300 suppress more javac warnings about unchecked casts 5 years ago
Github Actions ae9854df2b [skip ci] update natives snapshot 5 years ago
Stephen Gold 88c9371a4d avoid unchecked casts due by premature casting of Class objects 5 years ago
Stephen Gold 297443ada4 suppress javac warnings about unchecked casts in read() and write() 5 years ago
Stephen Gold 60967b9f04 IndexBuffer: resolve a javadoc warning 5 years ago
Stephen Gold a6f2c12700 jme3-terrain: handle 2 more ByteBuffer cases 5 years ago
ItsMike54 6c5611d3e2
Remove jogl (#1339) 5 years ago
matthias plasser 89df7909d3
Fixes #1341: AudioBuffer.updateData only allows direct buffers (#1342) 5 years ago
Jérôme b92e7a0abe
Migrate TestSkeletonControlRefresh to new anim system (#1334) 5 years ago
rvandoosselaer 26f393d09c Clean up the test case and add some more context. #1340 5 years ago
rvandoosselaer 2e1a8dcb52 Add a check to see if the NB_PROBES identifier is defined. Fixes #1340 5 years ago
rvandoosselaer 76210fe415 Add a check to see if the NB_PROBES identifier is defined. Fixes #1340 5 years ago
Jérôme b48391399b Using Tweens methods instead of the inner class 5 years ago
Jérôme 4c8e400a83
Migrate TestOgreAnim to new anim system (#1333) 5 years ago
Riccardo Balbo d109e4739e Fix last query not closed in profiler 5 years ago
Ali-RS 16a3e7630c
Fixed an issue with AndroidNativeImageLoader which was opening stream twice. (#1338) 5 years ago
Stephen Gold b8287a2d0d correct some typographic errors in Billboard100.frag comments 5 years ago
Stephen Gold fef5c101d8 elimiate incidental hard tabs from jme3-examples 5 years ago
Stephen Gold 51022fd521 elimiate incidental hard tabs from jme3-core 5 years ago
Jérôme b613be35fa Removes tabs 5 years ago
Jérôme 2a0fa2554a Migrate TestOgreComplexAnim to new anim system 5 years ago
Jérôme 3385cdaa3f Migrate TestBlenderObjectAnim to new anim system 5 years ago
Jérôme 1433f98ad3 Migrate TestBlenderAnim to the new anim system 5 years ago
Jérôme 11d3bc67f8 Migrate TestCutomAnim to the new Animation system 5 years ago
Jérôme bd4691e06b Migrate TestSpatialAnim to the new Anim class 5 years ago
Grabli66 fafe8a74fe Fixes error of loading assets 5 years ago
MeFisto94 efc1962092
Merge pull request #1320 from MeFisto94/fix-lwjgl3-double-stop 5 years ago
MeFisto94 989b2a66cf
Merge pull request #1322 from MeFisto94/fix-mesa-invalid-enum-ssbo 5 years ago
MeFisto94 0115616652 UBO: Re-order the caps detection for a more clean code. 5 years ago
MeFisto94 64797f9bff UBO: GL_INVALID_ENUM on Mesa 5 years ago
MeFisto94 21b7a71cf8 Fixes SSBO causing GL_INVALID_ENUM in Debug Mode 5 years ago
MeFisto94 8f8fc19043
Merge pull request #1321 from MeFisto94/fix-pbr-opengl2-part2 5 years ago
MeFisto94 646e6741b9 GLSLCompat: The Matrix3 Sub-Constructor is called mat3_sub and defers to the builtin mat3 if available. 5 years ago
MeFisto94 2276197a2b Fixing PBR on <= GLSL 130: 5 years ago
MeFisto94 2379cfba77
Merge pull request #1276 from MeFisto94/fix-gldebug 5 years ago
MeFisto94 df39677381 GLDebug: Throw an IllegalArgumentException instead of an IllegalStateException 5 years ago
MeFisto94 c198b1d546
Merge pull request #1318 from MeFisto94/fix-pbr-opengl2 5 years ago
MeFisto94 d19ae582a1 Fixes #1319 - LWJGL2: start(true) should not freeze when context could not be initialized 5 years ago
MeFisto94 461227bdef Fixes #1304 - Don't use mat3() constructor to keep compatibility with GLSL110 5 years ago
Riccardo Balbo 728a05c4f3 lambda 🎉 5 years ago
Ali-RS 0c6f240222
Check culling on the instanced geometries. (#1315) 5 years ago
MeFisto94 4d81d00f45 GLDebug: Throw an unchecked exception instead of returning null to enforce a crash when glGetError is not present. 5 years ago
MeFisto94 312fb5bb57 GLDebug: Use a method handle instead of a string comparison to increase the performance 5 years ago
Riccardo Balbo 7c01019f0c
Don't fail if there is no javadoc to commit. 5 years ago
Riccardo Balbo f505bdeb21
Update checkout action to v2 5 years ago
Daniel Perano ff58abca3d
Merge pull request #1306 from jMonkeyEngine/envcam-viewports 5 years ago
Toni Helenius 59793d4c06
Fix for resizing the window (issue 1191) (#1308) 5 years ago
Daniel Perano 0aa50a0f15 Expose ViewPorts 5 years ago
Stephen Gold 0252c1b623
README.md: add "Jumping Jack Flag" to game list 5 years ago
Stephen Gold a407fc3224 main.yml: add Gradle-wrapper validation to several jobs 5 years ago
Toni Helenius b93ea18fa2
Lwjgl3 restart input handle (#1268) 5 years ago
Lou Hamersly c1d359ca59
Fix #1289 Matrix3f/Matrix4f equals() should require an exact class match (#1302) 5 years ago
Stephen Gold 8e334aa756
BufferUtils: resolve issue #1288 (rewrite isDirect()) (#1299) 5 years ago
Github Actions 426969daeb [skip ci] update natives snapshot 5 years ago
Stephen Gold f652591281
remove unnecessary casts and tests for null, discovered using NetBeans (#1285) 5 years ago
MeFisto94 12481c08f6 Use a reflection-based approach to call checkError() after every call to the openGL API to reduce Code Duplication and increase Maintainability, while also fixing the regression caused by GLDebugDesktop extending from GLDebugES and thus making the Renderer think it is on mobile. 5 years ago
MeFisto94 ab96460853
Fix small things (#1287) 5 years ago
Stephen Gold f268d00222 Fixes issue #1286 5 years ago
Paul Speed 5584c93d4a
Merge pull request #1282 from stephengold/master 5 years ago
MeFisto94 8219d7fc02
Add two convenience flags to AppSettings: GraphicsTrace and GraphicsTiming. (#1279) 5 years ago
Stephen Gold dde0906963 re-assign Simsilica LLC copyrights with Paul's permission (issue #1001) 5 years ago
Github Actions e3b44db4aa [skip ci] update natives snapshot 5 years ago
Riccardo Balbo 31476679be Fix openal-soft dead link 5 years ago
Riccardo Balbo 0244ab230b
Update link for stb_image.h 5 years ago
Stephen Gold 1a0b6ecac3
jme3-blender: remove TextureGeneratorWood and its dependencies (#1274) 5 years ago
MeFisto94 acbddc2763
Merge pull request #1252 from MeFisto94/add-opengl31-lwjgl 5 years ago
Github Actions 3c5dd5c168 [skip ci] update natives snapshot 5 years ago
Stephen Gold 0fd70b81c9
Add missing @Override annotations (#1270) 5 years ago
Toni Helenius 427ae0a28b
Added Override annotations, finals, some cleanup and better null checks (#1271) 5 years ago
Ali-RS 2023440acf
Removed extra PostShadow Technique from PBRLighting.j3md (#1273) 5 years ago
Toni Helenius 867e46190e Issue 801 (#1269) 5 years ago
MeFisto94 eee37022f2 Fixes #1241 - Add a convenient Graphics Debug Option for the AppSettings (#1256) 5 years ago
MeFisto94 124ad35677 Fixes #1249 - Don't hang up when destroy is called multiple times (#1250) 5 years ago
Ryan McDonough 233bc6f0da PreShadow & PostShadow Support for PBRLighting.j3md (#1265) 5 years ago
Toni Helenius ffd9cfcf25 Upgrade Gradle 4.10 -> 5.6.4 (#1238) 5 years ago
Paul Speed 81f9b9d92a
Merge pull request #1262 from MeFisto94/fix-terrain-picker 5 years ago
MeFisto94 9b29e05968 Fixes #1261 - Clone the Terrain Picker, so that loading a terrain from file still works (after cloning, the picker would have the wrong terrain quad instance) 5 years ago
MeFisto94 933b0912e6
Merge pull request #1255 from MeFisto94/fix-bullet-natives 5 years ago
MeFisto94 ec491575be Fixes Bullet-Native Artifacts not containing natives when not building from cpp source and not using the build target. 5 years ago
MeFisto94 04e7bed5e7 Fixes #1251 - Support OpenGL3.1 on LWJGL2 and LWJGL3 5 years ago
Stephen Gold 77c521fefa
README.md: add pixelapp's Leap to list of games 5 years ago
Stephen Gold bc64238635 TestPBRLighting: fpp.setNumSamples() to facilitate study of issue #1246 5 years ago
Stephen Gold 3f59008566
README.md: add Nine Circles of Hell 5 years ago
Riccardo Balbo b059c7c0dd
Fix #1236 non-lvalue cannot be out parameter 5 years ago
joliver82 68fb1afe5d GLRenderer.modifyTexture on GLES fix (#1235) 5 years ago
Paul Speed b2ae269ede Updating the version.gradle to fix an issue with auto-versioning on the 5 years ago
Paul Speed 5db3ac4fac Refactored how versions are auto-built to provide more normal versions 5 years ago
Riccardo Balbo 124ef031d4
Remove "v" from version tag to maintain consistency with old releases 5 years ago
Stephen Gold cdcf0512d9
com.jme3.scene.shape.Line: protect the no-argument constructor (#1234) 5 years ago
Stephen Gold 6b7dd5b325
AnimControl: correct javadoc for the no-arg constructor (#1233) 5 years ago
Stephen Gold 8d9d091576
Mesh: avoid NPE in getMorphTargets() when there are no targets (#1231) 5 years ago
Stephen Gold 98f6d326e1
re-publicize InstancedGeometry no-arg constructor, correct erroneous javadoc (#1230) 5 years ago
Github Actions 584bb79392 [skip ci] update natives snapshot 5 years ago
Stephen Gold 75001d8abd
protect 52 no-arg constructors in the jme3-bullet/jme3-jbullet libraries (#1229) 5 years ago
Stephen Gold 9d2d393fc3
protect 47 no-arg constructors in the jme3-core library (#1228) 5 years ago
Paul Speed c73fd99dd6 Fixed the null child check to throw IllegalArgumentException instead of NullPointerException 5 years ago
Paul Speed c23f28b51c Modified Line to keep its own start/end instances. This is less of a surprise if 5 years ago
Paul Speed 45b1908906 Added an app state that will verify some of the JME constants (easy to expand later) 5 years ago
Paul Speed 37bb494709 Fixing the default versioning to be compatible with continued development. 5 years ago
joliver82 30df2f1b87 Android morph fix (#1221) 5 years ago
Matthew Universe 48f28974f9 Closes #1210 Quaternion.lookAt now has a return value (#1223) 5 years ago
Riccardo Balbo b07a11c9d4 Improve support for subimage copying. Allow to copy a part of an image to a texture2D. 5 years ago
joliver82 d935347bde Android buffer allocator implementation (#1214) 5 years ago
Riccardo Balbo 816ab99ac5 Fix version naming. Use version-branch-SNAPSHOT for local builds, add custom version name property. 5 years ago
Github Actions e304c5bb6d [skip ci] update natives snapshot 5 years ago
Riccardo Balbo 128e079a22 Update MacOS build: removed 32 bit support (since deprecated by apple). Update bullet library to the current master branch (needed for compatibility patch with recent clang). 5 years ago
joliver82 663c9776e8 Android openGL ES 3 support (#1147) 5 years ago
Ali-RS 7ae1ff23af
Fixed a typo 5 years ago
Riccardo Balbo d0186664f2 Fix extraction for non-unix paths. 5 years ago
Stephen Gold 92b161f38e fix issue #1202 5 years ago
Riccardo Balbo 3217cdc74c Remove migration code 5 years ago
Thodoris Sotiropoulos 4d734ac297 Separate getNativesZipFile and getPrebuiltNatives. Improve code quality. 5 years ago
Github Actions d8a42d0817 [skip ci] update natives snapshot 5 years ago
Riccardo Balbo 973d8be894 Hotfix expired certificate for macOS_SDK_headers_for_macOS_10.14 5 years ago
Riccardo Balbo b571840bd0 Fix missing bullet natives when running tests from within the engine project. 5 years ago
Ali-RS 2c6161ecd8 Added AnimComposer.hasAnimClip() 5 years ago
Ali-RS d4c9a9ad1e
Added Joint.getInitialTransform() (#1206) 5 years ago
Ali-RS 3b8665fe93
Fixed an issue with BlendableAction.setTransitionLength()... (#1205) 5 years ago
Ali-RS 5c75af1d38 AnimComposer: added getTime() and setTime() methods (#1201) 5 years ago
Github Actions 96b16884ce [skip ci] update natives snapshot 5 years ago
Riccardo Balbo a4c694ba36
Trigger rebuild 5 years ago
Riccardo Balbo 69ef8efcce Github Actions 5 years ago
Riccardo Balbo 88e9c8482c Store prebuild libraries outside the source folder, download prebuilt libraries from bintray 5 years ago
Riccardo Balbo 8a6bd1947c Remove fixed structure for version names 5 years ago
Riccardo Balbo 273f65b651 Fix toolchain. Add way to select wich platform you want to build. 5 years ago
MeFisto94 88385ba647 Build Bullet for Linux ARM (Raspberry Pi) 5 years ago
Ali-RS b1db497a00 Fix issue #1197: ClassCastException in LODGeomap.createMesh() (#1198) 5 years ago
Daniel Perano f312608725 Fixed ClassCastException in RMI implementation. (#1195) 5 years ago
Ali-RS f506e2a5a5 Updated mockito-core to v3.0.0 5 years ago
Ali-RS a6a35b370f Fix issue #1176: mergedJavadoc task fails on java 11 5 years ago
appveyor 3b6a602f96 [ci skip] bullet: update windows natives 5 years ago
Stephen Gold 62faa9a873 post-release cleanup 5 years ago
appveyor 84d3e0615d [ci skip] bullet: update windows natives 5 years ago
Stephen Gold bb32b882a0 gradle.properties: ready for 3.3.0-alpha5 build 5 years ago
Stephen Gold 2010c819cb travis: upload JARs and deploy from UPLOAD builds only 5 years ago
appveyor 4c8cd56c50 [ci skip] bullet: update windows natives 5 years ago
sgold b57d8239b4 gradle.properties: ready for 3.3.0-alpha4 build 5 years ago
sgold 52fbc17f4f travis: compile Linux natives on openjdk8, upload JARs from oraclejdk8 5 years ago
appveyor 1f02d87793 [ci skip] bullet: update windows natives 5 years ago
Stephen Gold 001b59344d gradle.properties: ready for 3.3.0-alpha3 build 5 years ago
Stephen Gold 0425c61dd4
fix for issue #1178 (BulletAppState violates AppState contract) (#1187) 5 years ago
Stephen Gold dcb66977ba fix issue #1188 (TestNiftyExamples crash) 5 years ago
Ali-RS 621a4ab6a9 Bump LWJGL3 version to '3.2.3' on jme3-vr as well 5 years ago
Ali-RS 981aa1d181 Bump LWJGL3 version to '3.2.3' 5 years ago
Stephen Gold 46a52af2f5 README.md: fix linkrot in the powered-by-JME list 5 years ago
travis-ci bc38b9b7ef [ci skip] bullet: update linux natives 5 years ago
Stephen Gold 89e753bf47
.travis.yml: use openjdk8 for the uploading build 5 years ago
Stephen Gold 3e89cd1377
define 2 new Platform values for Linux ARM, register BulletJme lib paths (#1182) 5 years ago
sgold 126874873c .travis.yml: try xenial dist for the oraclejdk8 build 5 years ago
travis-ci 6640436b03 [ci skip] update android natives 5 years ago
travis-ci cc9fcd7173 [ci skip] bullet: update osx natives 5 years ago
appveyor 8f6c88413f [ci skip] bullet: update windows natives 5 years ago
sgold 3482904c76 CollisionShape: add isNonMoving() and test buildscript 5 years ago
travis-ci 4ccdd7aae0 [ci skip] update android natives 5 years ago
sgold 814ab1b106 upload_native.sh: single commit per Travis CI job 5 years ago
travis-ci 4e56a6e24a [ci skip] bullet: update osx natives 5 years ago
appveyor 06c07fe0df [ci skip] bullet: update windows natives 5 years ago
Stephen Gold 42530de219 PhysicsCollisionObject: add getDeactivationTime() and test buildscript 5 years ago
Stephen Gold 2c2f76cca1 upload_native.sh: use "git diff" instead of "git diff-tree" 5 years ago
travis-ci 91f304272a [ci skip] bullet: update osx natives 5 years ago
appveyor 1484107c92 [ci skip] bullet: update windows natives 5 years ago
Stephen Gold 392a3c5cff SixDofJoint: add getAngles() and test recent buildscript changes 5 years ago
Stephen Gold 0f3bc6cdcf upload_native.sh: add debugging aids and comments 5 years ago
Stephen Gold 5eaf653de9
address issue #1119 (serialization with protected/private constructor) (#1181) 5 years ago
travis-ci 909ea30216 [ci skip] bullet: update osx natives 5 years ago
appveyor 637ccf91db [ci skip] bullet: update windows natives 5 years ago
Stephen Gold 30d1ecaec2 fix for issue #1157 (can't enable a TranslationalLimitMotor) 5 years ago
Stephen Gold 3c74fe539c
test and fix for issue #1138 (#1177) 5 years ago
MeFisto94 6aac1a21bd Continuous Integration: Change else-if to if, so that when multiple natives have been changed that multiple commits are issued. 5 years ago
MeFisto94 ad64b7e4b6 Continuous Integration: Fix regression in 21d0854b61. Variable was misnamed. 5 years ago
MeFisto94 21d0854b61 Continuous Integration: Also upload natives from bullet-native-android and android (#1171) 5 years ago
MeFisto94 c461a1b43d .travis.yml: remove deployment of android-natives from ubuntu build so they don't conflict with the android build matrix (#1171) 5 years ago
Stephen Gold 9aa25ad3ef
build.gradle: the clean task should remove merged javadoc 5 years ago
Stephen Gold 04953341d4
.travis.yml: continuous integration shouldn't need root privileges 5 years ago
mitm001 7a4e881bc6 Adding link to project tab. (#1170) 5 years ago
appveyor 970eb7fa15 [ci skip] bullet: update windows natives 5 years ago
MeFisto94 1bde90d1a1 Build android bullet-native on Travis (#1175) 5 years ago
Stephen Gold 194ae883c5
fix for issue #99 (pr #1082) 5 years ago
MeFisto94 3ec89ce499 Fix and Improve TerrainPicking (#1049) 5 years ago
Paul Speed 1ffd11fae4 Added a basic unit test for some of the AppStateManager functionality. 5 years ago
Paul Speed 36afe829c6 Part 2 of the small AppState refactoring: added methods to AppStateManager 5 years ago
Paul Speed 3dd2755c20 Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine 5 years ago
Paul Speed 3b8314a36f Part 1 of small refactoring to give AppState a unique ID for lookup purposes. 5 years ago
MeFisto94 90d3b69bd1 Build Android Natives (OpenAL, Vorbis, TextureLoading) on Travis (#1171) 5 years ago
Paul Speed 0d8fe2ac22 Created a LazyMessageBuffer class that differs from GreedyMessageBuffer only 5 years ago
Paul Speed 1c37d5a92d Refactored the base networking imlpementation's "MessageProtocol" to be an 5 years ago
Riccardo Balbo a9afcecc41
Recognize .glsl files as glsllib. 5 years ago
grizeldi 484d192467 Some fixes in jme-vr (#1163) 5 years ago
Ali-RS 3d7a5ee01b
Fixed an issue with previous commit 5 years ago
Ali-RS 740832d699
Renamed class to TestRtlBitmapText 5 years ago
Ali-RS 16ccd36d56
Rename TestRTLBitmapText.java to TestRtlBitmapText.java 5 years ago
Ali-RS 53839ca528
Added TestRTLBitmapText for issue #1158 (#1159) 5 years ago
Dima Myroniuk ae67b9c6f8 Update the link of the "Lightspeed Frontier". (#1160) 5 years ago
Ali-RS 467d7b2f0f Modified TransformTrack.jmeClone() return type to TransformTrack (#1155) 5 years ago
sgold 3fe896300d README.md: 3.2.4 is the latest stable version of the SDK 5 years ago
Lou Hamersly b864372256 Fix for NPE when running TestGImpactShape via TestChooser (#1151) 5 years ago
Stephen Gold acdeef54fe
.travis.yml: specify "precise" Linux distro for JDK8 build 5 years ago
empirephoenix 80aed88f82 fix for NPE when being called from CombinedTexture with null baseImage (#1146) 5 years ago
Lou Hamersly de092b92bb Wrap potential IOExceptions during native file extraction 5 years ago
travis-ci e31a047746 [ci skip] bullet: update linux natives 5 years ago
travis-ci 9f51de98a5 [ci skip] bullet: update osx natives 5 years ago
appveyor e7bf627e9d [ci skip] bullet: update windows natives 5 years ago
sgold 23ee02071e fix for issue #1141 5 years ago
Ali-RS 02ccce08ec
Updated CONTRIBUTING.md (#1139) 5 years ago
Trevor Flynn 57db8f618f Add support for gltf morph names. (#1100) 5 years ago
Riccardo Balbo 7058439e7d
Merge pull request #1132 from riccardobl/fixfspace 5 years ago
Sebastian Teumert 0ffc612bfb jme-core: IndexBuffer fixes & API improvements (#1136) 5 years ago
Lou H 827d4ebd52 Test for issue #1120 - JBullet GImpactShape fall-through (#1137) 5 years ago
travis-ci 1d5fe496e0 [ci skip] bullet: update linux natives 5 years ago
appveyor 69319738e6 [ci skip] bullet: update windows natives 5 years ago
travis-ci 918d90a637 [ci skip] bullet: update osx natives 5 years ago
Stephen Gold f831301a2a fix for issue #1135 (ConeJoint causes rigid body to disappear) 5 years ago
sgold 2ab0319202 jme3-examples: apply the "dirt_normal.png" normal map where appropriate 5 years ago
Lou H 8856ba7d25 GImpactShape Test Added (#1117) 5 years ago
sgold cff4dec57d README.md: a v3.2.3 SDK was released on 28 June 2019 5 years ago
Riccardo Balbo e603845fa5 Fix forced colorspace in materials when a matparamtexture value is changed. + unit test 6 years ago
Stephen Gold fca8bcf470 make native Bullet the default for jme3-examples (instead of JBullet) 6 years ago
Remy adb88dcaeb when the image object has a ColorSpace value we pass it on, else we leave it at the default value. fix for #1106 (#1110) 6 years ago
sgold 23759820e5 fix GImpact scaling in jme3-jbullet: issue #1120 6 years ago
travis-ci 12e513c8ee [ci skip] bullet: update linux natives 6 years ago
travis-ci ad6232c297 [ci skip] bullet: update osx natives 6 years ago
appveyor 5b9693f000 [ci skip] bullet: update windows natives 6 years ago
sgold 56d37cb866 fix GImpact scaling in jme3-bullet (native Bullet) 6 years ago
Stephen Gold 2cd3dac543 jme3-bullet fix for issue #1125 (physics shapes don't match TerrainQuad) 6 years ago
Stephen Gold df02333cd2 add test for issue #1125 (heightfield shapes don't match TerrainQuad) 6 years ago
Stephen Gold 5586c28c95 fix for issue #1116 (NPE in TerrainGridAlphaMapTest) 6 years ago
Lou H 1687d34eb7 #1107 - Javadoc warning cleanup & Fix for JDK 11 Javadoc NPE (#1118) 6 years ago
Lou H 1f3245b237 #1121-updateBound() call for PQTorus geometry updates (#1122) 6 years ago
Stephen Gold a91bcfd678 fix for issue #1127 ("Reset" action in TestHoveringTank) 6 years ago
Stephen Gold a4b22e4b37 fix for issue #1126 (TestHoveringTank assertion failures) 6 years ago
richardTingle 04f771f7f9 Issue 1113. Allow for Particle emitters that constantly have their particlesPerSec set to still emit (#1114) 6 years ago
Lou H f8dae05a52 #1107-Fix Javadoc warnings in OpenJDK 9+ (#1112) 6 years ago
Karan Nehra 9dff704a82 Attempt to fixing 1074 (#1093) 6 years ago
Lou Hamersly cde108c2c0 #1107-Fix Javadoc warnings in OpenJDK 9+ 6 years ago
Riccardo Balbo 97049630eb
Merge pull request #1109 from karanehra/fix-1051 6 years ago
Karan Nehra ddeb4549a2 Fix for 1051 6 years ago
Lou H 930090dfa0 #1097-Fixed inconsistent OpenGL version constants (#1099) 6 years ago
Lou Hamersly cf45e66d11 Fix Javadoc warnings in OpenJDK 9+ 6 years ago
James Khan b145268004
Merge pull request #1104 from jayfella/master 6 years ago
James Khan 82faaca87c Add unInterpolateLinear to FastMath class 6 years ago
James Khan a8f5ac0589
Merge pull request #1094 from jayfella/master 6 years ago
James Khan 1116df31c9 Add fog to Single-Pass lighting. 6 years ago
Riccardo Balbo a68d8b50e3
Add support for opengl versions > 3.2 in lwjgl2 renderer 6 years ago
lifeinwild 7e11a6c0fe Use UTF-8 in gradle builds 6 years ago
James Khan 4f47e5602b Add support for fog in materials. 6 years ago
appveyor a43ad8ecfe [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 5451e3eaf0 post-release fixup 6 years ago
Stephen Gold 3f1f2e8356 trivial commit with tag for v3.3.0-alpha2 6 years ago
Paul Speed 33ade6b874
Merge pull request #1087 from jayfella/master 6 years ago
Stephen Gold 6a71a1c273 trivial commit with tag 6 years ago
Stephen Gold ac01203c80 gradle.properties: ready for 3.3.0-alpha2 build 6 years ago
James Khan b9b4a2d75b Reference the joystick that changed state instead of the ID. 6 years ago
James Khan 2f6185b5cf Remove JoystickState enum and replace with boolean value. 6 years ago
James Khan d3be2f332f Use enum constants instead of integer ID's 6 years ago
James Khan 76fcc2c497 Remove magic number translations and remove uppercase names. 6 years ago
James Khan 682d8c9fd8 Use RawInputListener for InputManager instead of creating another field. 6 years ago
James Khan 80adca6dce Translate JoystickState directly from LWJGL context. 6 years ago
James Khan 318d6d0e89 Add support for listening to joystick connection/disconnection. 6 years ago
Ali-RS c4d2de1656
AnimComposer: updated to save globalSpeed on export and fixed issue with Layer cloning (#1080) 6 years ago
Stephen Gold 50aed6f59e
Travis: use "xenial" Linux dist to solve TLS issue (#1081) 6 years ago
Stephen Gold fff16c2328 TestIssue99: specify panel widths, clarify interpretation of results 6 years ago
Ali-RS 36ddb5b0ce
Added ambient light support in PBR shader (#1077) 6 years ago
Stephen Gold 40be1b42b8 DynamicAnimControl: remove all mention of attachments incl. isReleased() 6 years ago
Stephen Gold 432085c191 TestBoneRagdoll: reduce jumpiness during "toggle" actions 6 years ago
Stephen Gold 234bd476f3 implement contact-response for jme3-jbullet's PhysicsRigidBody 6 years ago
Remy 8d3980bbe9 Refactor collection to set in anim composer (#1076) 6 years ago
Stephen Gold 0bf931bf79 revert integration of PR #1071 which wasn't ready 6 years ago
Greg Hoffman 0bc060d474 Added an easy way to add sub steps to the profiler (#1067) 6 years ago
Remy ffa1df2aff returns unmodifiable sets (#1071) 6 years ago
Trevor Flynn 9c1452b63b Fixed classes location while building natives (JDK11 Support) (#1073) 6 years ago
appveyor 41f88cd5eb [ci skip] bullet: update windows natives 6 years ago
Trevor Flynn ad2ba95e99 Jdk 11 Build Support 6 years ago
remy 74588d96f3 adapt the caption of the string 6 years ago
Remy Van Doosselaer 988537987a calculate the total line height before aligning the letters, calculate the total line height from the font line height and total line count. Include a test case with alignment examples. fixes: #363 6 years ago
travis-ci 35b9d9df4e [ci skip] bullet: update linux natives 6 years ago
travis-ci 4bd8cfb218 [ci skip] bullet: update osx natives 6 years ago
appveyor 28cba18cbf [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 9e532009da fix for issue #1037 (TestHoveringTank hull-shape performance issue) 6 years ago
Stephen Gold ba340cf13a add a test case for issue #99 (blendMode="multiply" in NiftyGUI) 6 years ago
travis-ci 3696074c9a [ci skip] bullet: update linux natives 6 years ago
travis-ci 18cc15254b [ci skip] bullet: update osx natives 6 years ago
appveyor 73c055c37e [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 8a4e5e1177 fix issue #1060: bt32BitAxisSweep3 not implemented 6 years ago
Paul Speed 657c5841aa
Merge pull request #1066 from rvandoosselaer/fix-classcastexception-#1065 6 years ago
Remy Van Doosselaer 9a80f013fc fixed wrong reference check 6 years ago
Rickard Edén 1a1348983b
Merge pull request #1064 from FennelFetish/jme3-vr_mouse-fix 6 years ago
FennelFetish 88a9069662 Adds option to disable VR mouse cursor and thus enable the cursor on the desktop. 6 years ago
Riccardo Balbo 3b24067a3c Add RGTC format 6 years ago
Paul Speed 7c6e12ed70
Merge pull request #1062 from jMonkeyEngine/revert-1054-export-transparent-png 6 years ago
Ali-RS cd90be13ec
Revert "Modified JmeDesktopSystem.writeImageFile() to export transparency for…" 6 years ago
Stephen Gold d55776d081 revise TestBoneRagdoll & TestRagdollCharacter to use DynamicAnimControl 6 years ago
Stephen Gold 235b9db2ca publicize Joint.applyBindPose(), add DynamicAnimControl & related stuff 6 years ago
Paul Speed 59af265398 Updated the joystick test to indicate when the new LEFT_TRIGGER/RIGHT_TRIGGER 6 years ago
Paul Speed 54b812ca71 Standardized the XBox controller mappings to one XBOX set of entries 6 years ago
Paul Speed 5901a95363 Added new JoystickAxis.LEFT_TRIGGER/RIGHT_TRIGGER. 6 years ago
quazi-irfan 5bc64800e0 Fix printing if stencil is enable or disable 6 years ago
Paul Speed ac60a134d2 Updated the axis event processing to pay attention to dead zones. 6 years ago
Stephen Gold 27bd16979c
Armature.java: correct a typo in the javadoc 6 years ago
Stephen Gold f0d28a9501
changed Mythruna link to use http instead of https 6 years ago
Stephen Gold a2df09b2fd
add Spoxel to the list of games in the README.md 6 years ago
Riccardo Balbo 51cadb260c
Merge pull request #1050 from Diffblue-benchmarks/virtual-buffer-tests 6 years ago
Riccardo Balbo cf7b15bc23 j3md Fix for default textures (#1052) 6 years ago
Stephen Gold 1ad324aa57
fix issue #901 (collision margins initialized to 0) (#1053) 6 years ago
James Adam 7c2a401c28 Add tests for com.jme3/scene.mesh.VirtualIndexBuffer 6 years ago
Riccardo Balbo f60ff58ef0 Add bullet extraction directory, bin and vscode config to gitignore 6 years ago
Paul Speed 9a6032a087
Merge pull request #1054 from Ali-RS/export-transparent-png 6 years ago
Ali-RS c43eafc319
Modified JmeDesktopSystem.writeImageFile() to export transparency for PNG format. 6 years ago
appveyor 9f9b443dee [ci skip] bullet: update windows natives 6 years ago
Stephen Gold dc99bc0731 post-release fixup 6 years ago
Stephen Gold a6622d4613 trivial commit with tag 6 years ago
Stephen Gold 9d9132d5b6 .travis.yml: work around Travis's tag blindness using a regexp 6 years ago
appveyor 31574f1c65 [ci skip] bullet: update windows natives 6 years ago
Stephen Gold a03f1fe49c gradle.properties: ready for 3.3.0-alpha1 build 6 years ago
Quazi Irfan 76049c76f3 Add getSpatial to GhostControl, RigidBodyControl, AbstractPhysicsCont… (#1041) 6 years ago
Stephen Gold 2124e3e86b TestGltfLoading: comment 6 years ago
Stephen Gold b9c13a209a JavaDoc correction to AnimComposer (comments only) 6 years ago
Stephen Gold 7e929aacab FbxLoader: comment out some debug spam to System.out - see issue #1025 6 years ago
James Adam fe03a9b6a3 Add tests for com.jme3.math.Vector3f 6 years ago
James Adam eba9ef6d58 Add tests for com.jme3.math.FastMath 6 years ago
Ali-RS 6dd737d378 Added AnimComposer.getAction() and AnimComposer.addAction() and improved 6 years ago
Stephen Gold ffa58be3d7 remove more unused imports 6 years ago
Stephen Gold 75112201db more corrections in diagnostic messages 6 years ago
Stephen Gold 2356320d30 more comment-only correction 6 years ago
Riccardo Balbo 3851c35f08 Add blend mode that sums alphas and does alpha blending for colors 6 years ago
Stephen Gold 784106e0c6 Revert "fix for issue #989 (RollingTheMonkey works with JBullet but not native)" 6 years ago
travis-ci ff5124bc93 [ci skip] bullet: update linux natives 6 years ago
travis-ci d60aec411b [ci skip] bullet: update osx natives 6 years ago
appveyor f1d9eb37c4 [ci skip] bullet: update windows natives 6 years ago
Riccardo Balbo 2bbf784c96 fix issue #1029, integrate PR #1031 6 years ago
Stephen Gold 7e6fa03009 added a test for issue #1029 6 years ago
Stephen Gold f33252f4d0 spelling corrections in diagnostic messages 6 years ago
Stephen Gold 72bed74fa1 complete the fix for issue #1027 6 years ago
Stephen Gold 15f9f69611 fix for issue #1027 - result of rayTest() 6 years ago
Stephen Gold 4581b6cb36 jme3-examples/jme3-lwjgl3 JavaDoc corrections (comments only) 6 years ago
Stephen Gold c7c203853c jme3-networking/jme3-plugins JavaDoc corrections (comments only) 6 years ago
Stephen Gold 8d6900a32a jme3-jbullet JavaDoc corrections (comments only) 6 years ago
Stephen Gold 5bf619cca3 jme3-terrain JavaDoc corrections (comments only) 6 years ago
Stephen Gold b2381e8aed jme3-vr JavaDoc corrections (comments only) 6 years ago
Stephen Gold 7ad91651f1 jme3-desktop/jme3-effects JavaDoc corrections (comments only) 6 years ago
Stephen Gold c11fbe4dd8 jme3-core JavaDoc corrections (comments only) 6 years ago
Stephen Gold 8ecddf4fa2 jme3-blender JavaDoc corrections (comments only) 6 years ago
Stephen Gold ce730e5656 jme3-android JavaDoc corrections (comments only) 6 years ago
Stephen Gold 2c25d77e64 avoid extraneous newlines to stderr during glTF import 6 years ago
Stephen Gold 512b096cb4 fix for issue #1026: TestPhysicsCharacter spins Sinbad too fast 6 years ago
Stephen Gold 6ab7c0d91f fix for issue #910: PhysicsCharacter incompatibilities 6 years ago
Ali-RS dae85e1598 Removed unused imports. (#1020) 6 years ago
Ali-RS 6679873c9b Added removeAction(), removeLayer() and removeCurrentAction() to AnimComposer (#1016) 6 years ago
Ali-RS 9ded31cf1c Added TestUserData. (#1017) 6 years ago
Riccardo Balbo 626af3123a Add double short and byte support to UserData (#946) 6 years ago
Julien Seinturier 835fbd7957 Jme3 vr (#828) 6 years ago
Ali-RS 7363662f21 Added isSimilar() to Quaternion, Vector2f, Vector3f and Vector4f (#1015) 6 years ago
Ali-RS 637162e484 Added AnimComposer.hasAction() method 6 years ago
FennelFetish 254a5839c1 Added tests for traversal order when using SceneGraphVisitor. 6 years ago
FennelFetish 0f9cf090e7 Fixes depth-first-traversal order in Node. 6 years ago
NemesisMate 304550a0f4 Fix #324. Blender importer now respects the lightNode pos in the graph 6 years ago
Paul Speed 7b0471c43a
Merge pull request #980 from fba-rio/patch-2 6 years ago
Paul Speed d415cbb66b
Merge pull request #1010 from boonto/bugfix-float 6 years ago
Patrick Werner c7f6aa9ae6
fixed billboard shader bug on android where int is used instead of float 6 years ago
Ali-RS 7b346d2760 Bump LWJGL3 version to '3.2.1' 6 years ago
Stephen Gold 9d6d31d685 GuiEvent: add JavaDoc (comments only) 6 years ago
Stephen Gold 34b32bf29b fix for issue #1003 (Loading multiple GLB files with animations fails) 6 years ago
Stephen Gold d17376e713 PhysicsTestHelper: fix JavaDoc warnings (comments only) 6 years ago
Stephen Gold 51b12e1b9e test&fix for issue #1004 (RagdollUtils can't handle 16-bit bone indices) 6 years ago
Davis Rollman c6158bba68 Added getter and setter for LegacyApplication's speed (#986) 6 years ago
Stephen Gold 13d00e0df3 fix for issue #972 (TestModelExportingCloning fails) 6 years ago
Stephen Gold b9ea661391 fix for issue #993 (TestGltfLoading throws AssetNotFoundException) 6 years ago
Stephen Gold fceee58546 fix issue #997 (cloning UpdateControl throws IllegalArgumentException) 6 years ago
Stephen Gold 000ac95fd4 fix for issue #982: add town.zip file to repo, load different HTTP image 6 years ago
Stephen Gold e5fb5bfefe fix for issue #985: use a MaterialKey in TestAssetLinkNode 6 years ago
Stephen Gold 0e9ff83996 fix for issue #998: add ZIP file, remove "Jumps" and HttpZipLocator 6 years ago
Stephen Gold 7c6d828cb5
README.md: latest stable version is 3.2.2 6 years ago
Stephen Gold 8cd5894f55 TerrainTestTile: T key had to be pressed 2x before effect (see PR #827) 6 years ago
Stephen Gold 3c3e3f4af2 fixes for issue #994 (wildhouse.zip not found) 6 years ago
Stephen Gold 64d0e00a49 LwjglContext: correct a typo in a log message 6 years ago
Stephen Gold bbf1f73a89 fix for issue #983 (TestMusicStreaming crashes due to missing asset) 6 years ago
Stephen Gold 2a904034c2 fix for issue #992 (HelloCollision throws an AssetLoadException) 6 years ago
Toni Helenius 55d5cd7381 Updated Nifty to 1.4.3 and cleaned up the build 6 years ago
Stephen Gold 1bb388d793 fix for issue #989 (RollingTheMonkey works with JBullet but not native) 6 years ago
Stephen Gold 44801da706 fix for issue #988 (assertion failure in jme3test.games.CubeField) 6 years ago
Paul Speed 1f02567739
Merge pull request #987 from grizeldi/patch-2 6 years ago
grizeldi a88fdf99f0
More XBOX controller remappings 6 years ago
Francivan Bezerra 467af4fff5
Fixed call to onJoyButtonEvent() 6 years ago
Paul Speed f73d10bb9c
Merge pull request #979 from mitm001/master 6 years ago
mitm 416b866c78 Fixed a problem with mergeFrom() where a key that was explicitly mapped to null was taken to mean it had no mapping. Checking that the key has a mapping instead fixes problem. 6 years ago
mitm001 d785caa5ca
Merge pull request #2 from jMonkeyEngine/master 6 years ago
Stephen Gold 06d9194ddf
Update .travis.yml 6 years ago
Stephen Gold 627f4726e5
README.md: latest stable version is 3.2.1 6 years ago
mitm001 8a10738097
Merge pull request #1 from jMonkeyEngine/master 6 years ago
appveyor c22fac2dce [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 2d621ce24e
enable AppVeyor CI for v3.2 branch 6 years ago
Stephen Gold b525d6c00d
correct branch name, no longer need CI for v3.1 6 years ago
Stephen Gold 017800251b Revert "test and fix issue #358 (Centered BitmapText is rendered blurry)" 6 years ago
Stephen Gold 552f4a069c fix part B of issue #632 6 years ago
Stephen Gold c2337e823e fix part A of issue #632 6 years ago
Stephen Gold c4e1e7f1f8 test and fix issue #358 (Centered BitmapText is rendered blurry) 6 years ago
Stephen Gold cf7725a56e fix issue #240 (TestBlenderObjectAnim fails with NPE) 6 years ago
Stephen Gold 53d7052301
test and fix for issue #970 (#973) 6 years ago
Ali-RS a47b4a46ba Added a (hacky) fix for a Java Swing/AWT + GLFW interaction issue… (#968) 6 years ago
Stephen Gold d8acd30a27
implement setContactResponse(boolean) for PhysicsCharacter (issue #964) (#971) 6 years ago
travis-ci cde3c39c4f [ci skip] bullet: update linux natives 6 years ago
travis-ci 3d1dec7d77 [ci skip] bullet: update osx natives 6 years ago
appveyor 347191f6bd [ci skip] bullet: update windows natives 6 years ago
Stephen Gold cf33c3c2dd
implement setContactResponse(boolean) for PhysicsRigidBody (issue #964) (#966) 6 years ago
Paul Speed f44fdb35e6
Merge pull request #965 from Ali-RS/Ali-RS-patch-1 6 years ago
Ali-RS a61813ebd8
Added AppStateManager.getState(class, failOnMiss) 6 years ago
Ali-RS c0823bb903
Added a getState() method that will optionally fail on state misses. 6 years ago
appveyor d36deaf8ea [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 938c28fd2b
trigger AppVeyor build for additional changes 6 years ago
travis-ci 2ffebcf044 [ci skip] bullet: update linux natives 6 years ago
travis-ci a95cf8963f [ci skip] bullet: update osx natives 6 years ago
appveyor a66cb558c3 [ci skip] bullet: update windows natives 6 years ago
Stephen Gold e9069a0276
PhysicsSpace.cpp: whitespace changes to trigger an AppVeyor build (#963) 6 years ago
Stephen Gold 772af5ac60
edit gradle.properties to trigger a rebuild 6 years ago
Stephen Gold 7da493d1b1 AppVeyor: invalidate cached ZIP file if gradle.properties has changed 6 years ago
Stephen Gold d3e7d20374 upgrade native Bullet to v2.87, which should fix issue #877 6 years ago
MeFisto94 3d63433b28
Fixes #961 - Remove unused + deprecated Exception 6 years ago
Stephen Gold c707785a34 test and fix for JME issue #957 6 years ago
Paul Speed 53c788c031
Merge pull request #955 from stephengold/master 6 years ago
Stephen Gold 68ea05eaa6 test and fix for JME issue #954 (null bounding volume) 6 years ago
Stephen Gold c29a4bb1c6 fix for JME issue #887 (debug mesh ignores scaling) 6 years ago
Paul Speed d5bfe1e813 Modified the J3M loader to be a little less like a 1980s text adventure. 6 years ago
Paul Speed c2fe803f53 Added a nicer toString() to DirectionalLight. 6 years ago
joliver82 2b1c3d22e8 Update android bullet native libraries 6 years ago
joliver82 f19ad06820 Update android bullet native, mk files and gradle adding header generation dependency 6 years ago
Eike Foede 07c0df7f1d OrientedBoxProbeArea copy constructor fixed 6 years ago
JESTERRRRRR 7095ad10e0 FlyByCamera rise/fall behaviour (issue #617) 6 years ago
Stephen Gold 21454f2ab2 fix issue #923 for native Bullet 6 years ago
Rickard Edén a8b2ad0869
Merge pull request #937 from jseinturier/jme3-vr-openvr_lwjgl 6 years ago
Stephen Gold 8f21a7eb55 test and fix for issue #931 6 years ago
jseinturier 9661d8c747 Updating OpenVR bindings 6 years ago
JESTERRRRRR 45f7c3a230 Particle tile number/UV calculation change (#930) 6 years ago
travis-ci 03a5a4470a [ci skip] bullet: update linux natives 6 years ago
travis-ci 3f4d47559c [ci skip] bullet: update osx natives 6 years ago
appveyor 58eb2029c1 [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 976d45a230 test and fix for issue #928: disabled native Bullet's QuickProf 6 years ago
David Berrios 95ba65c8c5 Remove JVM arguments for OSX when running examples 6 years ago
Toni Helenius 989fc0a9f0 The mouse pos should be initialized always 6 years ago
Toni Helenius ca81975988 Get initial mouse pos on init, pass it to the InputManager 6 years ago
Toni Helenius 87023eb3f7 Set as finals 6 years ago
Nebloksiam e8cb3124f2 Load animated cursor NPE 6 years ago
Stephen Gold 02ec12e454 fix for issue #917 6 years ago
travis-ci c1c08f3a74 [ci skip] bullet: update linux natives 6 years ago
travis-ci d096f1be20 [ci skip] bullet: update osx natives 6 years ago
appveyor a492b746c2 [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 8f26a3cc3f tests and fixes for issues #918 and #919 6 years ago
Stephen Gold 697b4a675e resolve issue #916 6 years ago
Stephen Gold b2be9ce3f2
issue #915: use "precise" environment only for openjdk7 build 6 years ago
Stephen Gold ac088e4554
issue #915: revert Travis Linux build environment 6 years ago
travis-ci a31ee80798 [ci skip] bullet: update osx natives 6 years ago
appveyor b764f1346c [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 618337a81d fix for issue #913 6 years ago
travis-ci 499714507b [ci skip] bullet: update osx natives 6 years ago
appveyor 0fde1b86f8 [ci skip] bullet: update windows natives 6 years ago
Stephen Gold dc4189afc3 test case and fix for issue #911 6 years ago
Stephen Gold ce871712a8 fix for issue #908 6 years ago
Stephen Gold f16974fcb6 jme3-bullet: add and improve comments, mostly JavaDoc 6 years ago
Stephen Gold 84a103fbe4
fix for issue #740 (#906) 6 years ago
Stephen Gold 1c5c5e85c0
Merge pull request #905 from stephengold/master 6 years ago
Stephen Gold 6e96ee5bf2 jme3-bullet: add and improve comments, mostly JavaDoc 6 years ago
Stephen Gold 025b27c96d test cases and fix for issue #880 6 years ago
Stephen Gold 4323c4101a fix for issue #897 6 years ago
Stephen Gold e80302728f fix for issue #898 6 years ago
Stephen Gold 0b782b9fb4 fix for issue #886 6 years ago
Stephen Gold 10eef65e73 test case and fix for issue #894 6 years ago
Stephen Gold a4ca3cbd40 test case and fix for issue #883 6 years ago
Stephen Gold a28d5ccd16 test case and fix for issue #889 6 years ago
Riccardo Balbo f12d7e4e60 Fix AudioNode issues when using velocityFromTranslation and small refactoring (#875) 6 years ago
travis-ci 9c949891b0 [ci skip] bullet: update osx natives 6 years ago
appveyor 34126f25e4 [ci skip] bullet: update windows natives 6 years ago
Stephen Gold 570e4850ea rm auto-generated C++ headers from repo, rm unzipped files during clean 6 years ago
Stephen Gold 694232e269 use Gradle v4.10, android tools v3.1.4, bintray plugin v1.8.4 6 years ago
Stephen Gold 40f6a7cc7a avoid JDK10 so Travis CI can succeed 6 years ago
Stephen Gold ac182e38d5 add a test for JME issue #877 6 years ago
Riccardo Balbo 087197aff4 Fix bullet skipping enqueued tasks - #867 6 years ago
Stephen Gold 2d6aa15526 correct a typographical error in JavaDoc 6 years ago
Toni Helenius bbcb8c5701 Feature 871 (#873) 6 years ago
  1. 93
      .github/actions/tools/bintray.sh
  2. 85
      .github/actions/tools/uploadToMaven.sh
  3. 554
      .github/workflows/main.yml
  4. 23
      .gitignore
  5. 86
      .travis.yml
  6. 11
      CONTRIBUTING.md
  7. 29
      LICENSE
  8. 26
      README.md
  9. 58
      appveyor.yml
  10. 98
      build.gradle
  11. 2
      common-android-app.gradle
  12. 33
      common.gradle
  13. 35
      gradle.properties
  14. BIN
      gradle/wrapper/gradle-wrapper.jar
  15. 3
      gradle/wrapper/gradle-wrapper.properties
  16. 28
      gradlew
  17. 18
      gradlew.bat
  18. 13
      jme3-android-examples/build.gradle
  19. 6
      jme3-android-examples/src/main/java/jme3test/android/TestAndroidResources.java
  20. 16
      jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/JmeFragment.java
  21. 3
      jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java
  22. 3
      jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/TestActivity.java
  23. 2
      jme3-android-native/.gitignore
  24. 19
      jme3-android-native/decode.gradle
  25. BIN
      jme3-android-native/libs/decode/arm64-v8a/libdecodejme.so
  26. BIN
      jme3-android-native/libs/decode/armeabi-v7a/libdecodejme.so
  27. BIN
      jme3-android-native/libs/decode/armeabi/libdecodejme.so
  28. BIN
      jme3-android-native/libs/decode/mips/libdecodejme.so
  29. BIN
      jme3-android-native/libs/decode/mips64/libdecodejme.so
  30. BIN
      jme3-android-native/libs/decode/x86/libdecodejme.so
  31. BIN
      jme3-android-native/libs/decode/x86_64/libdecodejme.so
  32. BIN
      jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so
  33. BIN
      jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so
  34. BIN
      jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so
  35. BIN
      jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so
  36. BIN
      jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so
  37. BIN
      jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so
  38. BIN
      jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so
  39. 28
      jme3-android-native/openalsoft.gradle
  40. 2
      jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.c
  41. 2
      jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.c
  42. 2
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.c
  43. 173
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidAL.h
  44. 2
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.c
  45. 77
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidALC.h
  46. 2
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.c
  47. 101
      jme3-android-native/src/native/jme_openalsoft/com_jme3_audio_android_AndroidEFX.h
  48. 7
      jme3-android/build.gradle
  49. 16
      jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
  50. 23
      jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java
  51. 11
      jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java
  52. 4
      jme3-android/src/main/java/com/jme3/app/state/MjpegFileWriter.java
  53. 20
      jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
  54. 2
      jme3-android/src/main/java/com/jme3/asset/plugins/AndroidLocator.java
  55. 20
      jme3-android/src/main/java/com/jme3/audio/android/AndroidAL.java
  56. 8
      jme3-android/src/main/java/com/jme3/audio/android/AndroidALC.java
  57. 11
      jme3-android/src/main/java/com/jme3/audio/android/AndroidEFX.java
  58. 1
      jme3-android/src/main/java/com/jme3/audio/plugins/NativeVorbisLoader.java
  59. 1
      jme3-android/src/main/java/com/jme3/input/android/AndroidGestureProcessor.java
  60. 1
      jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
  61. 1
      jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler14.java
  62. 3
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInput.java
  63. 2
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoystickJoyInput14.java
  64. 4
      jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
  65. 6
      jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput.java
  66. 1
      jme3-android/src/main/java/com/jme3/input/android/AndroidTouchInput14.java
  67. 4
      jme3-android/src/main/java/com/jme3/input/android/TouchEventPool.java
  68. 182
      jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
  69. 4
      jme3-android/src/main/java/com/jme3/renderer/android/RendererUtil.java
  70. 20
      jme3-android/src/main/java/com/jme3/system/android/AndroidConfigChooser.java
  71. 1
      jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java
  72. 42
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
  73. 3
      jme3-android/src/main/java/com/jme3/texture/plugins/AndroidBufferImageLoader.java
  74. 3
      jme3-android/src/main/java/com/jme3/texture/plugins/AndroidNativeImageLoader.java
  75. 114
      jme3-android/src/main/java/com/jme3/util/AndroidBufferAllocator.java
  76. 4
      jme3-android/src/main/java/com/jme3/util/RingBuffer.java
  77. 12
      jme3-blender/build.gradle
  78. 733
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  79. 193
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  80. 770
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  81. 417
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  82. 391
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java
  83. 134
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java
  84. 400
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
  85. 133
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneEnvelope.java
  86. 317
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java
  87. 148
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
  88. 88
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  89. 188
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java
  90. 476
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  91. 397
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  92. 41
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java
  93. 32
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java
  94. 165
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java
  95. 162
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  96. 84
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java
  97. 126
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java
  98. 236
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  99. 107
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  100. 106
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,93 @@
#!/bin/bash
# bintray_createPackage [REPO] [PACKAGE] [USER] [PASSWORD] [GIT REPO] [LICENSE]
function bintray_createPackage {
repo="$1"
package="$2"
user="$3"
password="$4"
srcrepo="$5"
license="$6"
repoUrl="https://api.bintray.com/packages/$repo"
if [ "`curl -u$user:$password -H Content-Type:application/json -H Accept:application/json \
--write-out %{http_code} --silent --output /dev/null -X GET \"$repoUrl/$package\"`" != "200" ];
then
if [ "$srcrepo" != "" -a "$license" != "" ];
then
echo "Package does not exist... create."
data="{
\"name\": \"${package}\",
\"labels\": [],
\"licenses\": [\"${license}\"],
\"vcs_url\": \"${srcrepo}\"
}"
curl -u$user:$password -H "Content-Type:application/json" -H "Accept:application/json" -X POST \
-d "${data}" "$repoUrl"
else
echo "Package does not exist... you need to specify a repo and license for it to be created."
fi
else
echo "The package already exists. Skip."
fi
}
# uploadFile file destination [REPO] "content" [PACKAGE] [USER] [PASSWORD] [SRCREPO] [LICENSE]
function bintray_uploadFile {
file="$1"
dest="$2"
echo "Upload $file to $dest"
repo="$3"
type="$4"
package="$5"
user="$6"
password="$7"
srcrepo="$8"
license="$9"
publish="${10}"
bintray_createPackage $repo $package $user $password $srcrepo $license
url="https://api.bintray.com/$type/$repo/$package/$dest"
if [ "$publish" = "true" ]; then url="$url;publish=1"; fi
curl -T "$file" -u$user:$password "$url"
}
function bintray_uploadAll {
path="$1"
destpath="$2"
repo="$3"
type="$4"
package="$5"
user="$6"
password="$7"
srcrepo="$8"
license="$9"
publish="${10}"
cdir="$PWD"
cd "$path"
files="`find . -type f -print`"
IFS="
"
set -f
for f in $files; do
destfile="$destpath/${f:2}"
bintray_uploadFile $f $destfile $repo $type $package $user $password $srcrepo $license $publish
done
set +f
unset IFS
cd "$cdir"
}

@ -0,0 +1,85 @@
#!/bin/bash
#############################################
#
# Usage
# uploadAllToMaven path/of/dist/maven https://api.bintray.com/maven/riccardo/sandbox-maven/ riccardo $BINTRAY_PASSWORD gitrepo license
# Note: gitrepo and license are needed only when uploading to bintray if you want to create missing packages automatically
# gitrepo must be a valid source repository
# license must be a license supported by bintray eg "BSD 3-Clause"
# or
# uploadAllToMaven path/of/dist/maven $GITHUB_PACKAGE_REPOSITORY user password
#
#############################################
root="`dirname ${BASH_SOURCE[0]}`"
source $root/bintray.sh
set -e
function uploadToMaven {
file="$1"
destfile="$2"
repourl="$3"
user="$4"
password="$5"
srcrepo="$6"
license="$7"
auth=""
if [ "$user" != "token" ];
then
echo "Upload with username $user and password"
auth="-u$user:$password"
else
echo "Upload with token"
auth="-H \"Authorization: token $password\""
fi
if [[ $repourl == https\:\/\/api.bintray.com\/* ]];
then
package="`dirname $destfile`"
version="`basename $package`"
package="`dirname $package`"
package="`basename $package`"
if [ "$user" = "" -o "$password" = "" ];
then
echo "Error! You need username and password to upload to bintray"
exit 1
fi
echo "Detected bintray"
bintrayRepo="${repourl/https\:\/\/api.bintray.com\/maven/}"
echo "Create package on $bintrayRepo"
bintray_createPackage $bintrayRepo $package $user $password $srcrepo $license
repourl="$repourl/$package"
fi
cmd="curl -T \"$file\" $auth \
\"$repourl/$destfile\" \
-vvv"
echo "Run $cmd"
eval "$cmd"
}
export -f uploadToMaven
function uploadAllToMaven {
path="$1"
cdir="$PWD"
cd "$path"
files="`find . \( -name "*.jar" -o -name "*.pom" \) -type f -print`"
IFS="
"
set -f
for art in $files; do
art="${art:2}"
uploadToMaven "$art" "$art" ${@:2}
done
set +f
unset IFS
cd "$cdir"
}

@ -0,0 +1,554 @@
######################################################################################
# JME CI/CD
######################################################################################
# Quick overview of what is going on in this script:
# - Build natives for android
# - Build natives for linux arm
# - Build natives for windows,mac,linux x86_64 and x86
# - Merge the natives, build the engine, create the zip release, maven artifacts, javadoc and native snapshot
# - (only when there is a change in the native code) Deploy the native snapshot to bintray
# - (only when building a release) Deploy everything else to github releases, github packet registry and bintray
# - (only when building a release) Update javadoc.jmonkeyengine.org
# Note:
# All the actions/upload-artifact and actions/download-artifact steps are used to pass
# stuff between jobs, github actions has some sort of storage that is local to the
# running workflow, we use it to store the result of each job since the filesystem
# is not maintained between jobs.
################# CONFIGURATIONS #####################################################
# >> Configure BINTRAY RELEASE & NATIVE SNAPSHOT
# Configure the following secrets/variables (customize the values with your own)
# BINTRAY_GENERIC_REPO=riccardoblsandbox/jmonkeyengine-files
# BINTRAY_MAVEN_REPO=riccardoblsandbox/jmonkeyengine
# BINTRAY_USER=riccardo
# BINTRAY_APIKEY=XXXXXX
# BINTRAY_LICENSE="BSD 3-Clause"
# >> Configure PACKAGE REGISTRY RELEASE
# Nothing to do here, everything is autoconfigured to work with the account/org that
# is running the build.
# >> Configure JAVADOC
# JAVADOC_GHPAGES_REPO="riccardoblsandbox/javadoc.jmonkeyengine.org.git"
# Generate a deloy key
# ssh-keygen -t rsa -b 4096 -C "actions@users.noreply.github.com" -f javadoc_deploy
# Set
# JAVADOC_GHPAGES_DEPLOY_PRIVKEY="......."
# In github repo -> Settings, use javadoc_deploy.pub as Deploy key with write access
######################################################################################
# Resources:
# - Github actions docs: https://help.github.com/en/articles/about-github-actions
# - Package registry docs: https://help.github.com/en/articles/about-github-package-registry
# - Official actions: https://github.com/actions
# - Community actions: https://github.com/sdras/awesome-actions
######################################################################################
# - Riccardo Balbo
######################################################################################
name: Build jMonkeyEngine
on:
push:
branches:
- master
- newbuild
- v3.3.*
- v3.2
- v3.2.*
pull_request:
release:
types: [published]
jobs:
# Builds the natives on linux arm
BuildLinuxArmNatives:
name: Build natives for linux (arm)
runs-on: ubuntu-18.04
container:
image: riccardoblb/buildenv-jme3:linuxArm
steps:
- name: Clone the repo
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Validate the Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build
run: |
# Build
# Note: since this is crossbuild we use the buildForPlatforms filter to tell
# the buildscript wich platforms it should build for.
./gradlew -PuseCommitHashAsVersionName=true --no-daemon -PbuildForPlatforms=LinuxArm,LinuxArmHF,LinuxArm64 -PbuildNativeProjects=true \
:jme3-bullet-native:assemble
- name: Upload natives
uses: actions/upload-artifact@master
with:
name: linuxarm-natives
path: build/native
# Build the natives on android
BuildAndroidNatives:
name: Build natives for android
runs-on: ubuntu-18.04
container:
image: riccardoblb/buildenv-jme3:android
steps:
- name: Clone the repo
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Validate the Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build
run: |
./gradlew -PuseCommitHashAsVersionName=true --no-daemon -PbuildNativeProjects=true \
:jme3-android-native:assemble \
:jme3-bullet-native-android:assemble
- name: Upload natives
uses: actions/upload-artifact@master
with:
name: android-natives
path: build/native
# Build the natives
BuildNatives:
strategy:
fail-fast: true
matrix:
os: [ubuntu-18.04,windows-2019,macOS-latest]
jdk: [8.x.x]
include:
- os: ubuntu-18.04
osName: linux
- os: windows-2019
osName: windows
- os: macOS-latest
osName: mac
name: Build natives for ${{ matrix.osName }}
runs-on: ${{ matrix.os }}
steps:
- name: Clone the repo
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Prepare java environment
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.jdk }}
architecture: x64
- name: Validate the Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build Natives
shell: bash
env:
OS_NAME: ${{ matrix.osName }}
run: |
# Install dependencies
if [ "$OS_NAME" = "mac" ];
then
echo "Prepare mac"
elif [ "$OS_NAME" = "linux" ];
then
echo "Prepare linux"
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib
else
echo "Prepare windows"
fi
# Build
./gradlew -PuseCommitHashAsVersionName=true --no-daemon -PbuildNativeProjects=true -Dmaven.repo.local="$PWD/dist/maven" \
build \
:jme3-bullet-native:build
# Upload natives to be used later by the BuildJMonkey job
- name: Upload natives
uses: actions/upload-artifact@master
with:
name: ${{ matrix.osName }}-natives
path: build/native
# Build the engine, we only deploy from ubuntu-18.04 jdk8
BuildJMonkey:
needs: [BuildNatives,BuildAndroidNatives]
name: Build on ${{ matrix.osName }} jdk${{ matrix.jdk }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-18.04,windows-2019,macOS-latest]
jdk: [8.x.x,11.x.x]
include:
- os: ubuntu-18.04
osName: linux
deploy: true
- os: windows-2019
osName: windows
- os: macOS-latest
osName: mac
- jdk: 11.x.x
deploy: false
steps:
- name: Clone the repo
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Setup the java environment
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.jdk }}
architecture: x64
- name: Download natives for linux
uses: actions/download-artifact@master
with:
name: linux-natives
path: build/native
- name: Download natives for windows
uses: actions/download-artifact@master
with:
name: windows-natives
path: build/native
- name: Download natives for mac
uses: actions/download-artifact@master
with:
name: mac-natives
path: build/native
- name: Download natives for android
uses: actions/download-artifact@master
with:
name: android-natives
path: build/native
- name: Download natives for linux (arm)
uses: actions/download-artifact@master
with:
name: linuxarm-natives
path: build/native
- name: Validate the Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build Engine
shell: bash
run: |
# Build
./gradlew -PuseCommitHashAsVersionName=true -PskipPrebuildLibraries=true build
if [ "${{ matrix.deploy }}" = "true" ];
then
# We are going to need "zip"
sudo apt-get update
sudo apt-get install -y zip
# Create the zip release and the javadoc
./gradlew -PuseCommitHashAsVersionName=true -PskipPrebuildLibraries=true mergedJavadoc createZipDistribution
# We prepare the release for deploy
mkdir -p ./dist/release/
mv build/distributions/*.zip dist/release/
# Create the maven artifacts
mkdir -p ./dist/maven/
./gradlew -PuseCommitHashAsVersionName=true -PskipPrebuildLibraries=true install -Dmaven.repo.local="$PWD/dist/maven"
# Zip the natives into a single archive (we are going to use this to deploy native snapshots)
echo "Create native zip"
cdir="$PWD"
cd "build/native"
zip -r "$cdir/dist/jme3-natives.zip" *
cd "$cdir"
echo "Done"
fi
# Used later by DeploySnapshot
- name: Upload merged natives
if: matrix.deploy==true
uses: actions/upload-artifact@master
with:
name: natives
path: dist/jme3-natives.zip
# Upload maven artifacts to be used later by the deploy job
- name: Upload maven artifacts
if: matrix.deploy==true
uses: actions/upload-artifact@master
with:
name: maven
path: dist/maven
- name: Upload javadoc
if: matrix.deploy==true
uses: actions/upload-artifact@master
with:
name: javadoc
path: dist/javadoc
# Upload release archive to be used later by the deploy job
- name: Upload release
if: github.event_name == 'release' && matrix.deploy==true
uses: actions/upload-artifact@master
with:
name: release
path: dist/release
# This job deploys the native snapshot.
# The snapshot is downloaded when people build the engine without setting buildNativeProject
# this is useful for people that want to build only the java part and don't have
# all the stuff needed to compile natives.
DeploySnapshot:
needs: [BuildJMonkey]
name: "Deploy snapshot"
runs-on: ubuntu-18.04
if: github.event_name == 'push'
steps:
# We clone the repo manually, since we are going to push back a reference to the snapshot
- name: Clone the repo
run: |
branch="${GITHUB_REF//refs\/heads\//}"
if [ "$branch" != "" ];
then
git clone --single-branch --branch "$branch" https://github.com/${GITHUB_REPOSITORY}.git .
fi
- name: Download merged natives
uses: actions/download-artifact@master
with:
name: natives
path: dist/
- name: Deploy natives snapshot
run: |
source .github/actions/tools/bintray.sh
NATIVE_CHANGES="yes"
branch="${GITHUB_REF//refs\/heads\//}"
if [ "$branch" != "" ];
then
if [ -f "natives-snapshot.properties" ];
then
nativeSnapshot=`cat "natives-snapshot.properties"`
nativeSnapshot="${nativeSnapshot#*=}"
# We deploy ONLY if GITHUB_SHA (the current commit hash) is newer than $nativeSnapshot
if [ "`git rev-list --count $nativeSnapshot..$GITHUB_SHA`" = "0" ];
then
NATIVE_CHANGES=""
else
# We check if the native code changed.
echo "Detect changes"
NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- jme3-bullet-native/)"
if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- jme3-android-native/)"; fi
if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- jme3-bullet-native-android/)"; fi
if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- jme3-bullet/)"; fi
# The bulletUrl (in gradle.properties) might have changed.
if [ "$NATIVE_CHANGES" = "" ];then NATIVE_CHANGES="$(git diff-tree --name-only "$GITHUB_SHA" "$nativeSnapshot" -- gradle.properties)"; fi
fi
fi
# We do nothing if there is no change
if [ "$NATIVE_CHANGES" = "" ];
then
echo "No changes, skip."
else
if [ "${{ secrets.BINTRAY_GENERIC_REPO }}" = "" ];
then
echo "Configure the following secrets to enable native snapshot deployment"
echo "BINTRAY_GENERIC_REPO, BINTRAY_USER, BINTRAY_APIKEY"
else
# Deploy snapshot
bintray_uploadFile dist/jme3-natives.zip \
$GITHUB_SHA/$GITHUB_SHA/jme3-natives.zip \
${{ secrets.BINTRAY_GENERIC_REPO }} "content" "natives" \
${{ secrets.BINTRAY_USER }} \
${{ secrets.BINTRAY_APIKEY }} \
"https://github.com/${GITHUB_REPOSITORY}" \
"${{ secrets.BINTRAY_LICENSE }}" "true"
# We reference the snapshot by writing its commit hash in natives-snapshot.properties
echo "natives.snapshot=$GITHUB_SHA" > natives-snapshot.properties
# We commit the updated natives-snapshot.properties
git config --global user.name "Github Actions"
git config --global user.email "actions@users.noreply.github.com"
git add natives-snapshot.properties
git commit -m "[skip ci] update natives snapshot"
# Pull rebase from the remote repo, just in case there was a push in the meantime
git pull -q --rebase
# We need to calculate the header for git authentication
header=$(echo -n "ad-m:${{ secrets.GITHUB_TOKEN }}" | base64)
# Push
(git -c http.extraheader="AUTHORIZATION: basic $header" push origin "$branch" || true)
fi
fi
fi
# This job deploys the release
DeployRelease:
needs: [BuildJMonkey]
name: Deploy Release
runs-on: ubuntu-18.04
if: github.event_name == 'release'
steps:
# We need to clone everything again for uploadToMaven.sh ...
- name: Clone the repo
uses: actions/checkout@v2
with:
fetch-depth: 1
# Download all the stuff...
- name: Download maven artifacts
uses: actions/download-artifact@master
with:
name: maven
path: dist/maven
- name: Download release
uses: actions/download-artifact@master
with:
name: release
path: dist/release
- name: Deploy to github releases
run: |
# We need to get the release id (yeah, it's not the same as the tag)
echo "${GITHUB_EVENT_PATH}"
cat ${GITHUB_EVENT_PATH}
releaseId=$(jq --raw-output '.release.id' ${GITHUB_EVENT_PATH})
# Now that we have the id, we just upload the release zip from before
echo "Upload to release $releaseId"
filename="$(ls dist/release/*.zip)"
url="https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/$releaseId/assets?name=$(basename $filename)"
echo "Upload to $url"
curl -L \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/zip" \
--data-binary @"$filename" \
"$url"
- name: Deploy to bintray
run: |
source .github/actions/tools/uploadToMaven.sh
if [ "${{ secrets.BINTRAY_MAVEN_REPO }}" = "" ];
then
echo "Configure the following secrets to enable bintray deployment"
echo "BINTRAY_MAVEN_REPO, BINTRAY_USER, BINTRAY_APIKEY"
else
uploadAllToMaven dist/maven/ https://api.bintray.com/maven/${{ secrets.BINTRAY_MAVEN_REPO }} ${{ secrets.BINTRAY_USER }} ${{ secrets.BINTRAY_APIKEY }} "https://github.com/${GITHUB_REPOSITORY}" "${{ secrets.BINTRAY_LICENSE }}"
fi
# - name: Deploy to github package registry
# run: |
# source .github/actions/tools/uploadToMaven.sh
# registry="https://maven.pkg.github.com/$GITHUB_REPOSITORY"
# echo "Deploy to github package registry $registry"
# uploadAllToMaven dist/maven/ $registry "token" ${{ secrets.GITHUB_TOKEN }}
# Deploy the javadoc
DeployJavaDoc:
needs: [BuildJMonkey]
name: Deploy Javadoc
runs-on: ubuntu-18.04
if: github.event_name == 'release'
steps:
# We are going to need a deploy key for this, since we need
# to push to a different repo
- name: Set ssh key
run: |
mkdir -p ~/.ssh/
echo "${{ secrets.JAVADOC_GHPAGES_DEPLOY_PRIVKEY }}" > $HOME/.ssh/deploy.key
chmod 600 $HOME/.ssh/deploy.key
# We clone the javadoc repo
- name: Clone gh-pages
run: |
branch="gh-pages"
export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $HOME/.ssh/deploy.key"
git clone --single-branch --branch "$branch" git@github.com:${{ secrets.JAVADOC_GHPAGES_REPO }} .
# Download the javadoc in the new directory "newdoc"
- name: Download javadoc
uses: actions/download-artifact@master
with:
name: javadoc
path: newdoc
# The actual deploy
- name: Deploy to github pages
run: |
set -f
IFS=$'\n'
# Get the tag for this release
version="`if [[ $GITHUB_REF == refs\/tags* ]]; then echo ${GITHUB_REF//refs\/tags\//}; fi`"
# If there is no tag, then we do nothing.
if [ "$version" != "" ];
then
echo "Deploy as $version"
# Remove any older version of the javadoc for this tag
if [ -d "$version" ];then rm -Rf "$version"; fi
# Rename newdoc with the version name
mv newdoc "$version"
# if there isn't an index.txt we create one (we need this to list the versions)
if [ ! -f "index.txt" ]; then echo "" > index.txt ; fi
index="`cat index.txt`"
# Check if this version is already in index.txt
addNew=true
for v in $index;
do
if [ "$v" = "$version" ];
then
echo "$v" "$version"
addNew=false
break
fi
done
# If not, we add it to the beginning
if [ "$addNew" = "true" ];
then
echo -e "$version\n$index" > index.txt
index="`cat index.txt`"
fi
# Regenerate the pages
chmod +x make.sh
./make.sh
# Configure git to use the deploy key
export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $HOME/.ssh/deploy.key"
# Commit the changes
git config --global user.name "Github Actions"
git config --global user.email "actions@users.noreply.github.com"
git add . || true
git commit -m "$version" || true
branch="gh-pages"
git push origin "$branch" --force || true
fi

23
.gitignore vendored

@ -2,6 +2,7 @@
**/.classpath **/.classpath
**/.settings **/.settings
**/.project **/.project
**/.vscode
**/out/ **/out/
/.gradle/ /.gradle/
/.nb-gradle/ /.nb-gradle/
@ -18,30 +19,30 @@
*.jnilib *.jnilib
*.dylib *.dylib
*.iml *.iml
*.class
*.jtxt
.gradletasknamecache .gradletasknamecache
.DS_Store .DS_Store
/jme3-core/src/main/resources/com/jme3/system/version.properties /jme3-core/src/main/resources/com/jme3/system/version.properties
/jme3-*/build/ /jme3-*/build/
/jme3-bullet-native/bullet.zip /jme3-*/bin/
/jme3-bullet-native/bullet3.zip /jme3-bullet-native/bullet3.zip
/jme3-bullet-native/bullet-2.82-r2704/ /jme3-bullet-native/bullet3-*/
/jme3-bullet-native/bullet3-2.83.7/ /jme3-bullet-native/src/native/cpp/com_jme3_bullet_*.h
/jme3-android-native/openal-soft/ /jme3-android-native/openal-soft/
/jme3-android-native/OpenALSoft.zip /jme3-android-native/OpenALSoft.zip
/jme3-android-native/src/native/jme_decode/STBI/ /jme3-android-native/src/native/jme_decode/STBI/
/jme3-android-native/src/native/jme_decode/Tremor/ /jme3-android-native/src/native/jme_decode/Tremor/
/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.h
/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.h
/jme3-android-native/stb_image.h /jme3-android-native/stb_image.h
!/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll /jme3-examples/private/
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
!/jme3-vr/src/main/resources/**/*.dylib !/jme3-vr/src/main/resources/**/*.dylib
!/jme3-vr/src/main/resources/**/*.so !/jme3-vr/src/main/resources/**/*.so
!/jme3-vr/src/main/resources/**/*.so.dbg !/jme3-vr/src/main/resources/**/*.so.dbg
!/jme3-vr/src/main/resources/**/*.dll !/jme3-vr/src/main/resources/**/*.dll
!/jme3-vr/src/main/resources/**/*.pdb !/jme3-vr/src/main/resources/**/*.pdb
/buildMaven.bat /buildMaven.bat
/private
.travis.yml
appveyor.yml
javadoc_deploy
javadoc_deploy.pub

@ -1,86 +0,0 @@
language: java
sudo: false
dist: precise
branches:
only:
- master
- v3.1
- /^v3.2.0-.*$/
matrix:
include:
- os: linux
jdk: oraclejdk8
env: UPLOAD=true UPLOAD_NATIVE=true
- os: linux
jdk: openjdk7
- os: osx
env: UPLOAD_NATIVE=true
addons:
ssh_known_hosts: github.com
hosts:
- travisci
hostname: travisci
apt:
packages:
- gcc-multilib
- g++-multilib
before_install:
- '[ -n "$UPLOAD" ] && git fetch --unshallow || :'
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
install:
- '[ -n "$UPLOAD_NATIVE" ] && ./gradlew -PbuildNativeProjects=true assemble || ./gradlew assemble'
script:
- ./gradlew check
after_success:
- '[ "$TRAVIS_PULL_REQUEST" == "false" ] && [ -n "$UPLOAD_NATIVE" ] && ./private/upload_native.sh || :'
- '[ -n "$TRAVIS_TAG" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ -n "$UPLOAD" ] && ./gradlew bintrayUpload || :'
notifications:
slack:
on_success: change
on_failure: always
rooms:
secure: "PWEk4+VL986c3gAjWp12nqyifvxCjBqKoESG9d7zWh1uiTLadTHhZJRMdsye36FCpz/c/Jt7zCRO/5y7FaubQptnRrkrRfjp5f99MJRzQVXnUAM+y385qVkXKRKd/PLpM7XPm4AvjvxHCyvzX2wamRvul/TekaXKB9Ti5FCN87s="
before_deploy:
- ./gradlew createZipDistribution
- export RELEASE_DIST=$(ls build/distributions/*.zip)
deploy:
provider: releases
api_key:
secure: PuEsJd6juXBH29ByITW3ntSAyrwWs0IeFvXJ5Y2YlhojhSMtTwkoWeB6YmDJWP4fhzbajk4TQ1HlOX2IxJXSW/8ShOEIUlGXz9fHiST0dkSM+iRAUgC5enCLW5ITPTiem7eY9ZhS9miIam7ngce9jHNMh75PTzZrEJtezoALT9w=
file_glob: true
file: "${RELEASE_DIST}"
skip_cleanup: true
on:
repo: jMonkeyEngine/jmonkeyengine
tags: true
# before_install:
# required libs for android build tools
# sudo apt-get update
# sudo apt-get install -qq p7zip-full
# sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs ia32-libs-multiarch
# newest Android NDK
# wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin -O ndk.bin
# 7z x ndk.bin -y > /dev/null
# export ANDROID_NDK=`pwd`/android-ndk-r10c

@ -9,6 +9,8 @@ Communication always comes first. **All** code changes and other contributions s
### New Contributors ### New Contributors
Check out the [Projects](https://github.com/jMonkeyEngine/jmonkeyengine/projects/1) tab, where the team has prioritized issues that you as a new contributor can undertake that will familiarize you to the workflow of contributing. This highlights some issues the team thinks would be a good start for new contributors but you are free to contribute on any other issues or integration you wish.
When you're ready to submit your code, just make a [pull request](https://help.github.com/articles/using-pull-requests). When you're ready to submit your code, just make a [pull request](https://help.github.com/articles/using-pull-requests).
- Do not commit your code until you have received proper feedback. - Do not commit your code until you have received proper feedback.
@ -22,6 +24,15 @@ p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/
Developers in the Contributors team can push directly to Main instead of submitting pull requests, however for new features it is often a good idea to do a pull request as a means to get a last code review. Developers in the Contributors team can push directly to Main instead of submitting pull requests, however for new features it is often a good idea to do a pull request as a means to get a last code review.
## Customs around integration, branching, tagging, and releases
- Most pull requests are integrated directly into the master branch of the repository.
- Integrators should note, unless the history of the pull request is important, it should be integrated to a single commit using “squash and merge”. If the history is important, favor “rebase and merge”. Don’t create a merge commit unless GitHub cannot rebase the PR.
- For each major release (such as v3.0 or v3.3), an appropriately named release branch is created in the repository.
- For each minor (or “dot-dot”) release (such as v3.2.3), an appropriately named tag is created in the repository.
- In general, library changes that plausibly might break existing apps appear only in major releases, not minor ones.
## Building the engine ## Building the engine
1. Install [Gradle](http://www.gradle.org/) 1. Install [Gradle](http://www.gradle.org/)

@ -0,0 +1,29 @@
Copyright (c) 2009-2020 jMonkeyEngine
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of 'jMonkeyEngine' nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,9 +1,9 @@
jMonkeyEngine jMonkeyEngine
============= =============
[![Build Status](https://travis-ci.org/jMonkeyEngine/jmonkeyengine.svg?branch=master)](https://travis-ci.org/jMonkeyEngine/jmonkeyengine) [![Build Status](https://github.com/jMonkeyEngine/jmonkeyengine/workflows/Build%20jMonkeyEngine/badge.svg)](https://github.com/jMonkeyEngine/jmonkeyengine/actions)
jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.0 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives. jMonkeyEngine is a 3-D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.4 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives.
The engine is used by several commercial game studios and computer-science courses. Here's a taste: The engine is used by several commercial game studios and computer-science courses. Here's a taste:
@ -11,16 +11,18 @@ The engine is used by several commercial game studios and computer-science cours
- [jME powered games on IndieDB](http://www.indiedb.com/engines/jmonkeyengine/games) - [jME powered games on IndieDB](http://www.indiedb.com/engines/jmonkeyengine/games)
- [Maker's Tale](http://steamcommunity.com/sharedfiles/filedetails/?id=93461954t) - [Maker's Tale](http://steamcommunity.com/sharedfiles/filedetails/?id=93461954t)
- [Boardtastic 2](https://play.google.com/store/apps/details?id=com.boardtastic.skateboarding) - [Boardtastic 2](https://boardtastic-2.fileplanet.com/apk)
- [Copod](http://herebeben.com/copod) - [Attack of the Gelatinous Blob](https://attack-gelatinous-blob.softwareandgames.com/)
- [Attack of the Gelatinous Blob](http://attackofthegelatinousblob.com/) - [Mythruna](http://mythruna.com/)
- [Chaos](http://4realms.net/) - [PirateHell (on Steam)](https://store.steampowered.com/app/321080/Pirate_Hell/)
- [Mythruna](https://mythruna.com/) - [3089 (on Steam)](http://store.steampowered.com/app/263360/)
- [PirateHell](http://www.desura.com/games/piratehell) - [3079 (on Steam)](http://store.steampowered.com/app/259620/)
- [3089 (on steam)](http://store.steampowered.com/app/263360/) - [Lightspeed Frontier (on Steam)](https://store.steampowered.com/app/548650/Lightspeed_Frontier/)
- [3079 (on steam)](http://store.steampowered.com/app/259620/)
- [Lightspeed Frontier](http://www.lightspeedfrontier.com/)
- [Skullstone](http://www.skullstonegame.com/) - [Skullstone](http://www.skullstonegame.com/)
- [Spoxel (on Steam)](https://store.steampowered.com/app/746880/Spoxel/)
- [Nine Circles of Hell (on Steam)](https://store.steampowered.com/app/1200600/Nine_Circles_of_Hell/)
- [Leap](https://gamejolt.com/games/leap/313308)
- [Jumping Jack Flag](http://timealias.bplaced.net/jack/)
## Getting started ## Getting started
@ -35,7 +37,7 @@ Note: The master branch on GitHub is a development version of the engine and is
- NetBeans Platform - NetBeans Platform
- Gradle - Gradle
Plus a bunch of awesome libraries & tight integrations like Bullet, Blender, NiftyGUI and other goodies. Plus a bunch of awesome libraries & tight integrations like Bullet, NiftyGUI and other goodies.
### Documentation ### Documentation

@ -1,58 +0,0 @@
version: 1.0.{build}.{branch}
branches:
only:
- master
only_commits:
files:
- jme3-bullet-native/
skip_tags: true
max_jobs: 1
clone_depth: 1
image: Visual Studio 2013
environment:
encrypted_f0a0b284e2e8_iv:
secure: aImQXs4g7zMXm1nWRvlh2wPK1UQvozS1fOVNthpyoEDFZ2FvBSdXqh5NPbGh44+F
encrypted_f0a0b284e2e8_key:
secure: Ek2lqC2e19qQDRRdlvnYyLFBq3TNj6YwKTAPuJ2VElJsxi9lQg+9ZP+VbP4kbHTx6Zaa++vtmOuxLZL7gdILrEEPa1Jix2BBLBfcxBUxe6w=
install:
- cmd: >-
set GRADLE_LOCK=C:\Users\appveyor\.gradle\caches\modules-2\modules-2.lock
if exist %GRADLE_LOCK% del %GRADLE_LOCK%
build_script:
- cmd: gradlew.bat -PbuildNativeProjects=true :jme3-bullet-native:assemble
cache:
- C:\Users\appveyor\.gradle\caches
- C:\Users\appveyor\.gradle\wrapper
- jme3-bullet-native\bullet3.zip
test: off
deploy: off
on_success:
- cmd: >-
openssl aes-256-cbc -K %encrypted_f0a0b284e2e8_key% -iv %encrypted_f0a0b284e2e8_iv% -in private\key.enc -out c:\users\appveyor\.ssh\id_rsa -d
git config --global user.email "appveyor"
git config --global user.name "appveyor"
git checkout -q %APPVEYOR_REPO_BRANCH%
git add -- jme3-bullet-native/libs/native/windows/
git commit -m "[ci skip] bullet: update windows natives"
git pull -q --rebase
git push git@github.com:jMonkeyEngine/jmonkeyengine.git

@ -1,16 +1,30 @@
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
buildscript { buildscript {
repositories { repositories {
google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.5' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath 'me.tatarka:gradle-retrolambda:3.7.1'
}
}
allprojects {
repositories {
google()
jcenter()
} }
} }
apply plugin: 'base' apply plugin: 'base'
apply from: file('version.gradle') apply from: file('version.gradle')
apply plugin: 'me.tatarka.retrolambda'
// This is applied to all sub projects // This is applied to all sub projects
subprojects { subprojects {
if(!project.name.equals('jme3-android-examples')) { if(!project.name.equals('jme3-android-examples')) {
@ -101,8 +115,8 @@ task mergedJavadoc(type: Javadoc, description: 'Creates Javadoc from all the pro
source subprojects.collect {project -> source subprojects.collect {project ->
project.sourceSets*.allJava project.sourceSets*.allJava
} }
// classpath = files(subprojects.collect {project -> classpath = files(subprojects.collect {project ->
// project.sourceSets*.compileClasspath}) project.sourceSets*.compileClasspath})
// source { // source {
// subprojects*.sourceSets*.main*.allSource // subprojects*.sourceSets*.main*.allSource
// } // }
@ -111,12 +125,13 @@ task mergedJavadoc(type: Javadoc, description: 'Creates Javadoc from all the pro
} }
} }
task mergedSource(type: Copy){ clean.dependsOn('cleanMergedJavadoc')
task cleanMergedJavadoc(type: Delete) {
delete file('dist/javadoc')
} }
task wrapper(type: Wrapper, description: 'Creates and deploys the Gradle wrapper to the current directory.') { task mergedSource(type: Copy){
gradleVersion = '4.1'
} }
ext { ext {
@ -144,6 +159,62 @@ task configureAndroidNDK {
} }
} }
gradle.rootProject.ext.set("usePrebuildNatives", buildNativeProjects!="true");
if (skipPrebuildLibraries != "true" && buildNativeProjects != "true") {
String rootPath = rootProject.projectDir.absolutePath
Properties nativesSnasphotProp = new Properties()
File nativesSnasphotPropF = new File("${rootPath}/natives-snapshot.properties");
if (nativesSnasphotPropF.exists()) {
nativesSnasphotPropF.withInputStream { nativesSnasphotProp.load(it) }
String nativesSnasphot = nativesSnasphotProp.getProperty("natives.snapshot");
String nativesUrl = PREBUILD_NATIVES_URL.replace('${natives.snapshot}', nativesSnasphot)
println "Use natives snapshot: " + nativesUrl
String nativesZipFile = "${rootPath}" + File.separator + "build" + File.separator + nativesSnasphot + "-natives.zip"
String nativesPath = "${rootPath}" + File.separator + "build" + File.separator + "native"
task getNativesZipFile {
outputs.file nativesZipFile
doFirst {
File target = file(nativesZipFile);
println("Download natives from " + nativesUrl + " to " + nativesZipFile);
target.getParentFile().mkdirs();
ant.get(src: nativesUrl, dest: target);
}
}
task extractPrebuiltNatives {
inputs.file nativesZipFile
outputs.dir nativesPath
dependsOn getNativesZipFile
doFirst {
for (File src : zipTree(nativesZipFile)) {
String srcRel = src.getAbsolutePath().substring((int) (nativesZipFile.length() + 1));
srcRel = srcRel.substring(srcRel.indexOf(File.separator) + 1);
File dest = new File(nativesPath + File.separator + srcRel);
boolean doCopy = !(dest.exists() && dest.lastModified() > src.lastModified())
if (doCopy) {
println("Copy " + src + " " + dest);
dest.getParentFile().mkdirs();
Files.copy(src.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
assemble.dependsOn extractPrebuiltNatives
}
}
//class IncrementalReverseTask extends DefaultTask { //class IncrementalReverseTask extends DefaultTask {
// @InputDirectory // @InputDirectory
// def File inputDir // def File inputDir
@ -179,3 +250,14 @@ task configureAndroidNDK {
// enableAssertions = true // true by default // enableAssertions = true // true by default
// } // }
//} //}
wrapper {
gradleVersion = '5.6.4'
}
retrolambda {
javaVersion JavaVersion.VERSION_1_7
incremental true
jvmArgs '-noverify'
}

@ -1,7 +1,7 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
group = 'org.jmonkeyengine' group = 'org.jmonkeyengine'
version = jmeVersion + '-' + jmeVersionTag version = jmeFullVersion
sourceCompatibility = '1.6' sourceCompatibility = '1.6'

@ -3,14 +3,21 @@
// //
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'maven' apply plugin: 'maven'
group = 'org.jmonkeyengine' group = 'org.jmonkeyengine'
version = jmePomVersion version = jmeFullVersion
sourceCompatibility = '1.7' sourceCompatibility = '1.8'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
gradle.projectsEvaluated {
tasks.withType(JavaCompile) { // compile-time options:
options.compilerArgs << '-Xlint:unchecked'
}
}
repositories { repositories {
mavenCentral() mavenCentral()
maven { maven {
@ -24,9 +31,21 @@ repositories {
dependencies { dependencies {
// Adding dependencies here will add the dependencies to each subproject. // Adding dependencies here will add the dependencies to each subproject.
testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.0.28-beta' testCompile group: 'org.mockito', name: 'mockito-core', version: '3.0.0'
testCompile group: 'org.easytesting', name: 'fest-assert-core', version: '2.0M10' testCompile group: 'org.easytesting', name: 'fest-assert-core', version: '2.0M10'
testCompile 'org.codehaus.groovy:groovy-all:2.5.8'
}
// Uncomment if you want to see the status of every test that is run and
// the test output.
/*
test {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
}
} }
*/
jar { jar {
manifest { manifest {
@ -38,11 +57,13 @@ jar {
javadoc { javadoc {
failOnError = false failOnError = false
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.docTitle = "jMonkeyEngine ${jmeMainVersion} ${project.name} Javadoc" options.docTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc"
options.windowTitle = "jMonkeyEngine ${jmeMainVersion} ${project.name} Javadoc" options.windowTitle = "jMonkeyEngine ${jmeFullVersion} ${project.name} Javadoc"
options.header = "<b>jMonkeyEngine ${jmeMainVersion} ${project.name}</b>" options.header = "<b>jMonkeyEngine ${jmeFullVersion} ${project.name}</b>"
options.author = "true" options.author = "true"
options.use = "true" options.use = "true"
options.charSet = "UTF-8"
options.encoding = "UTF-8"
//disable doclint for JDK8, more quiet output //disable doclint for JDK8, more quiet output
if (JavaVersion.current().isJava8Compatible()){ if (JavaVersion.current().isJava8Compatible()){
options.addStringOption('Xdoclint:none', '-quiet') options.addStringOption('Xdoclint:none', '-quiet')

@ -1,11 +1,16 @@
# Version number used for plugins, only 3 numbers (e.g. 3.1.3) # Version number: Major.Minor.SubMinor (e.g. 3.3.0)
jmeVersion = 3.3.0 jmeVersion = 3.4.0
# Version used for application and settings folder, no spaces!
jmeMainVersion = 3.3 # Leave empty to autogenerate
# Version addition pre-alpha-svn, Stable, Beta # (use -PjmeVersionName="myVersion" from commandline to specify a custom version name )
jmeVersionTag = SNAPSHOT jmeVersionName =
# Increment this each time jmeVersionTag changes but jmeVersion stays the same
jmeVersionTagID = 0 # If true, the version name will contain the commit hash
useCommitHashAsVersionName = false
# Set to true if a non-master branch name should be included in the automatically
# generated version.
includeBranchInVersion = false
# specify if JavaDoc should be built # specify if JavaDoc should be built
buildJavaDoc = true buildJavaDoc = true
@ -14,18 +19,22 @@ buildJavaDoc = true
buildNativeProjects = false buildNativeProjects = false
buildAndroidExamples = false buildAndroidExamples = false
buildForPlatforms = Linux64,Linux32,Windows64,Windows32,Mac64
# Forcefully ignore prebuilt libraries
skipPrebuildLibraries=false
# Path to android NDK for building native libraries # Path to android NDK for building native libraries
#ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7 #ndkPath=/Users/normenhansen/Documents/Code-Import/android-ndk-r7
ndkPath = /opt/android-ndk-r10c ndkPath = /opt/android-ndk-r16b
# Path for downloading native Bullet # Path for downloading native Bullet
bulletUrl = https://github.com/bulletphysics/bullet3/archive/2.86.1.zip # 2.89+ (circa 26 April 2020, to avoid jMonkeyEngine issue #1283)
bulletFolder = bullet3-2.86.1 bulletUrl = https://github.com/bulletphysics/bullet3/archive/cd8cf7521cbb8b7808126a6adebd47bb83ea166a.zip
bulletFolder = bullet3-cd8cf7521cbb8b7808126a6adebd47bb83ea166a
bulletZipFile = bullet3.zip bulletZipFile = bullet3.zip
# POM settings # POM settings
POM_NAME=jMonkeyEngine POM_NAME=jMonkeyEngine
POM_DESCRIPTION=jMonkeyEngine is a 3D game engine for adventurous Java developers POM_DESCRIPTION=jMonkeyEngine is a 3-D game engine for adventurous Java developers
POM_URL=http://jmonkeyengine.org POM_URL=http://jmonkeyengine.org
POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine POM_SCM_URL=https://github.com/jMonkeyEngine/jmonkeyengine
POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git POM_SCM_CONNECTION=scm:git:git://github.com/jMonkeyEngine/jmonkeyengine.git
@ -38,3 +47,5 @@ POM_INCEPTION_YEAR=2009
# Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline # Bintray settings to override in $HOME/.gradle/gradle.properties or ENV or commandline
bintray_user= bintray_user=
bintray_api_key= bintray_api_key=
PREBUILD_NATIVES_URL=https://dl.bintray.com/jmonkeyengine/files/${natives.snapshot}/jme3-natives.zip

Binary file not shown.

@ -1,6 +1,5 @@
#Sun Sep 17 22:55:30 EDT 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip

28
gradlew vendored

@ -1,5 +1,21 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
## Gradle start up script for UN*X ## Gradle start up script for UN*X
@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
warn ( ) { warn () {
echo "$*" echo "$*"
} }
die ( ) { die () {
echo echo
echo "$*" echo "$*"
echo echo
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
@ -155,7 +171,7 @@ if $cygwin ; then
fi fi
# Escape application args # Escape application args
save ( ) { save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }

18
gradlew.bat vendored

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS= set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome

@ -1,8 +1,8 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 23 compileSdkVersion 28
buildToolsVersion "23.0.3" buildToolsVersion "28.0.3"
lintOptions { lintOptions {
// Fix nifty gui referencing "java.awt" package. // Fix nifty gui referencing "java.awt" package.
@ -13,7 +13,7 @@ android {
defaultConfig { defaultConfig {
applicationId "org.jmonkeyengine.jme3androidexamples" applicationId "org.jmonkeyengine.jme3androidexamples"
minSdkVersion 15 // Android 4.0.3 ICE CREAM SANDWICH minSdkVersion 15 // Android 4.0.3 ICE CREAM SANDWICH
targetSdkVersion 22 // Android 5.1 LOLLIPOP targetSdkVersion 28 // Android 9 PIE
versionCode 1 versionCode 1
versionName "1.0" // TODO: from settings.gradle versionName "1.0" // TODO: from settings.gradle
} }
@ -25,6 +25,11 @@ android {
} }
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets { sourceSets {
main { main {
java { java {
@ -42,7 +47,7 @@ android {
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.android.support:appcompat-v7:28.0.0'
compile project(':jme3-core') compile project(':jme3-core')
compile project(':jme3-android') compile project(':jme3-android')

@ -1,15 +1,9 @@
package jme3test.android; package jme3test.android;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.light.AmbientLight;
import com.jme3.light.PointLight;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.util.TangentBinormalGenerator;
/** /**
* Test case to look for images stored in the Android drawable and mipmap directories. Image files are * Test case to look for images stored in the Android drawable and mipmap directories. Image files are

@ -1,16 +1,12 @@
package org.jmonkeyengine.jme3androidexamples; package org.jmonkeyengine.jme3androidexamples;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import com.jme3.app.AndroidHarnessFragment; import com.jme3.app.AndroidHarnessFragment;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogManager; import java.util.logging.LogManager;
import static org.jmonkeyengine.jme3androidexamples.MainActivity.*;
/** /**
* A placeholder fragment containing a the jME GLSurfaceView. * A placeholder fragment containing a jME GLSurfaceView.
*/ */
public class JmeFragment extends AndroidHarnessFragment { public class JmeFragment extends AndroidHarnessFragment {
@ -66,15 +62,15 @@ public class JmeFragment extends AndroidHarnessFragment {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Bundle bundle=getArguments(); Bundle bundle=getArguments();
appClass = bundle.getString(SELECTED_APP_CLASS); appClass = bundle.getString(MainActivity.SELECTED_APP_CLASS);
// Log.d(this.getClass().getSimpleName(), "AppClass: " + appClass); // Log.d(this.getClass().getSimpleName(), "AppClass: " + appClass);
joystickEventsEnabled = bundle.getBoolean(ENABLE_JOYSTICK_EVENTS); joystickEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_JOYSTICK_EVENTS);
// Log.d(this.getClass().getSimpleName(), "JoystickEventsEnabled: " + joystickEventsEnabled); // Log.d(this.getClass().getSimpleName(), "JoystickEventsEnabled: " + joystickEventsEnabled);
keyEventsEnabled = bundle.getBoolean(ENABLE_KEY_EVENTS); keyEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_KEY_EVENTS);
// Log.d(this.getClass().getSimpleName(), "KeyEventsEnabled: " + keyEventsEnabled); // Log.d(this.getClass().getSimpleName(), "KeyEventsEnabled: " + keyEventsEnabled);
mouseEventsEnabled = bundle.getBoolean(ENABLE_MOUSE_EVENTS); mouseEventsEnabled = bundle.getBoolean(MainActivity.ENABLE_MOUSE_EVENTS);
// Log.d(this.getClass().getSimpleName(), "MouseEventsEnabled: " + mouseEventsEnabled); // Log.d(this.getClass().getSimpleName(), "MouseEventsEnabled: " + mouseEventsEnabled);
boolean verboseLogging = bundle.getBoolean(VERBOSE_LOGGING); boolean verboseLogging = bundle.getBoolean(MainActivity.VERBOSE_LOGGING);
// Log.d(this.getClass().getSimpleName(), "VerboseLogging: " + verboseLogging); // Log.d(this.getClass().getSimpleName(), "VerboseLogging: " + verboseLogging);
if (verboseLogging) { if (verboseLogging) {
// Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info) // Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info)

@ -1,6 +1,5 @@
package org.jmonkeyengine.jme3androidexamples; package org.jmonkeyengine.jme3androidexamples;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.os.Bundle; import android.os.Bundle;
@ -287,7 +286,7 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
private boolean checkClassType(String className) { private boolean checkClassType(String className) {
boolean include = true; boolean include = true;
try { try {
Class<?> clazz = (Class<?>) Class.forName(className); Class<?> clazz = Class.forName(className);
if (Application.class.isAssignableFrom(clazz)) { if (Application.class.isAssignableFrom(clazz)) {
Log.d(TAG, "Class " + className + " is a jME Application"); Log.d(TAG, "Class " + className + " is a jME Application");
} else { } else {

@ -1,9 +1,8 @@
package org.jmonkeyengine.jme3androidexamples; package org.jmonkeyengine.jme3androidexamples;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.support.v7.app.AppCompatActivity;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;

@ -0,0 +1,2 @@
# The headers are autogenerated and nobody should try to commit them...
src/native/headers

@ -1,16 +1,19 @@
String tremorZipFile = "TremorAndroid.zip" String tremorZipFile = "TremorAndroid.zip"
String stbiUrl = 'https://raw.githubusercontent.com/nothings/stb/master/stb_image.h' String stbiUrl = 'https://raw.githubusercontent.com/jMonkeyEngine/stb/0224a44a10564a214595797b4c88323f79a5f934/stb_image.h'
// Working directories for the ndk build. // Working directories for the ndk build.
String decodeBuildDir = "${buildDir}" + File.separator + 'decode' String decodeBuildDir = "${buildDir}" + File.separator + 'decode'
String decodeClassesBuildDir = "${buildDir}" + File.separator + 'decode_classes'
String decodeBuildJniDir = decodeBuildDir + File.separator + 'jni' String decodeBuildJniDir = decodeBuildDir + File.separator + 'jni'
String decodeBuildLibsDir = decodeBuildDir + File.separator + 'libs' String decodeBuildLibsDir = decodeBuildDir + File.separator + 'libs'
// Pre-compiled libs directory // Pre-compiled libs directory
String decodePreCompiledLibsDir = 'libs' + File.separator + 'decode' def rootPath = rootProject.projectDir.absolutePath
String decodePreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator + 'android' + File.separator + 'decode'
// jME Android Native source files path // jME Android Native source files path
String decodeSourceDir = 'src/native/jme_decode' String decodeSourceDir = 'src/native/jme_decode'
String jmeHeaders = 'src/native/headers'
task downloadStbImage(type: MyDownload) { task downloadStbImage(type: MyDownload) {
sourceUrl = stbiUrl sourceUrl = stbiUrl
@ -44,17 +47,13 @@ task copyTremorFiles(type: Copy) {
into outputDir into outputDir
} }
// Generate headers via javah task copyJmeHeadersDecode(type: Copy) {
task generateJavahHeaders(type: Exec) { from file(jmeHeaders)
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah') into file(decodeBuildJniDir + File.separator + "headers")
args '-d', decodeSourceDir
args '-classpath', project.projectClassPath
args "com.jme3.audio.plugins.NativeVorbisFile"
args "com.jme3.texture.plugins.AndroidNativeImageLoader"
} }
// Copy jME Android native files to jni directory // Copy jME Android native files to jni directory
task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, generateJavahHeaders]) { task copySourceToBuild(type: Copy, dependsOn:[copyTremorFiles, copyStbiFiles, copyJmeHeadersDecode]) {
def sourceDir = file(decodeSourceDir) def sourceDir = file(decodeSourceDir)
def outputDir = file(decodeBuildJniDir) def outputDir = file(decodeBuildJniDir)

@ -1,22 +1,25 @@
// OpenAL Soft r1.16 // OpenAL Soft r1.16
String openALSoftUrl = 'http://repo.or.cz/w/openal-soft.git/snapshot/e5016f814a265ed592a88acea95cf912c4bfdf12.zip' String openALSoftUrl = 'https://github.com/jMonkeyEngine/openal-soft/archive/e5016f814a265ed592a88acea95cf912c4bfdf12.zip'
String openALSoftZipFile = 'OpenALSoft.zip' String openALSoftZipFile = 'OpenALSoft.zip'
// OpenAL Soft directory the download is extracted into // OpenAL Soft directory the download is extracted into
// Typically, the downloaded OpenAL Soft zip file will extract to a directory // Typically, the downloaded OpenAL Soft zip file will extract to a directory
// called "openal-soft" // called "openal-soft"
String openALSoftFolder = 'openal-soft-e5016f8' String openALSoftFolder = 'openal-soft-e5016f814a265ed592a88acea95cf912c4bfdf12'
//Working directories for the ndk build. //Working directories for the ndk build.
String openalsoftBuildDir = "${buildDir}" + File.separator + 'openalsoft' String openalsoftBuildDir = "${buildDir}" + File.separator + 'openalsoft'
String openalsoftClassesBuildDir = "${buildDir}" + File.separator + 'openalsoft_classes'
String openalsoftBuildJniDir = openalsoftBuildDir + File.separator + 'jni' String openalsoftBuildJniDir = openalsoftBuildDir + File.separator + 'jni'
String openalsoftBuildLibsDir = openalsoftBuildDir + File.separator + 'libs' String openalsoftBuildLibsDir = openalsoftBuildDir + File.separator + 'libs'
//Pre-compiled libs directory //Pre-compiled libs directory
String openalsoftPreCompiledLibsDir = 'libs' + File.separator + 'openalsoft' def rootPath = rootProject.projectDir.absolutePath
String openalsoftPreCompiledLibsDir = rootPath + File.separator + 'build' + File.separator + 'native' + File.separator + 'android' + File.separator + 'openalsoft'
// jME Android Native source files path // jME Android Native source files path
String openalsoftJmeAndroidPath = 'src/native/jme_openalsoft' String openalsoftJmeAndroidPath = 'src/native/jme_openalsoft'
String jmeHeaders = 'src/native/headers'
// Download external source files if not available // Download external source files if not available
task downloadOpenALSoft(type: MyDownload) { task downloadOpenALSoft(type: MyDownload) {
@ -61,8 +64,14 @@ copyOpenALSoft.dependsOn {
} }
} }
// Copy JME Headers to jni directory
task copyJmeHeadersOpenAL(type: Copy) {
from file(jmeHeaders)
into file(openalsoftBuildJniDir + File.separator + "headers")
}
// Copy jME Android native files to jni directory // Copy jME Android native files to jni directory
task copyJmeOpenALSoft(type: Copy, dependsOn:copyOpenALSoft) { task copyJmeOpenALSoft(type: Copy, dependsOn: [copyOpenALSoft, copyJmeHeadersOpenAL]) {
def sourceDir = file(openalsoftJmeAndroidPath) def sourceDir = file(openalsoftJmeAndroidPath)
def outputDir = file(openalsoftBuildJniDir) def outputDir = file(openalsoftBuildJniDir)
// println "copyJmeOpenALSoft sourceDir: " + sourceDir // println "copyJmeOpenALSoft sourceDir: " + sourceDir
@ -72,16 +81,7 @@ task copyJmeOpenALSoft(type: Copy, dependsOn:copyOpenALSoft) {
into outputDir into outputDir
} }
task generateOpenAlSoftHeaders(type:Exec, dependsOn: copyJmeOpenALSoft) { task buildOpenAlSoftNativeLib(type: Exec, dependsOn: copyJmeOpenALSoft) {
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
args '-d', openalsoftJmeAndroidPath
args '-classpath', project.projectClassPath
args "com.jme3.audio.android.AndroidAL"
args "com.jme3.audio.android.AndroidALC"
args "com.jme3.audio.android.AndroidEFX"
}
task buildOpenAlSoftNativeLib(type: Exec, dependsOn: generateOpenAlSoftHeaders) {
// println "openalsoft build dir: " + openalsoftBuildDir // println "openalsoft build dir: " + openalsoftBuildDir
// println "ndkCommandPath: " + project.ndkCommandPath // println "ndkCommandPath: " + project.ndkCommandPath
workingDir openalsoftBuildDir workingDir openalsoftBuildDir

@ -4,7 +4,7 @@
#include "Tremor/ivorbisfile.h" #include "Tremor/ivorbisfile.h"
#include "com_jme3_audio_plugins_NativeVorbisFile.h" #include "../headers/com_jme3_audio_plugins_NativeVorbisFile.h"
#ifndef NDEBUG #ifndef NDEBUG
#include <android/log.h> #include <android/log.h>

@ -1,4 +1,4 @@
#include "com_jme3_texture_plugins_AndroidNativeImageLoader.h" #include "../headers/com_jme3_texture_plugins_AndroidNativeImageLoader.h"
#include <assert.h> #include <assert.h>
#ifndef NDEBUG #ifndef NDEBUG

@ -1,4 +1,4 @@
#include "com_jme3_audio_android_AndroidAL.h" #include "../headers/com_jme3_audio_android_AndroidAL.h"
#include "AL/al.h" #include "AL/al.h"
#include "AL/alext.h" #include "AL/alext.h"

@ -1,173 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jme3_audio_android_AndroidAL */
#ifndef _Included_com_jme3_audio_android_AndroidAL
#define _Included_com_jme3_audio_android_AndroidAL
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alGetString
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidAL_alGetString
(JNIEnv *, jobject, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alGenSources
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGenSources
(JNIEnv *, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alGetError
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGetError
(JNIEnv *, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alDeleteSources
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alDeleteSources
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alGenBuffers
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alGenBuffers
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alDeleteBuffers
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alDeleteBuffers
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourceStop
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceStop
(JNIEnv *, jobject, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourcei
* Signature: (III)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcei
(JNIEnv *, jobject, jint, jint, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alBufferData
* Signature: (IILjava/nio/ByteBuffer;II)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alBufferData
(JNIEnv *, jobject, jint, jint, jobject, jint, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourcePlay
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcePlay
(JNIEnv *, jobject, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourcePause
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcePause
(JNIEnv *, jobject, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourcef
* Signature: (IIF)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourcef
(JNIEnv *, jobject, jint, jint, jfloat);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSource3f
* Signature: (IIFFF)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSource3f
(JNIEnv *, jobject, jint, jint, jfloat, jfloat, jfloat);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alGetSourcei
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidAL_alGetSourcei
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourceUnqueueBuffers
* Signature: (IILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceUnqueueBuffers
(JNIEnv *, jobject, jint, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSourceQueueBuffers
* Signature: (IILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSourceQueueBuffers
(JNIEnv *, jobject, jint, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alListener
* Signature: (ILjava/nio/FloatBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListener
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alListenerf
* Signature: (IF)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListenerf
(JNIEnv *, jobject, jint, jfloat);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alListener3f
* Signature: (IFFF)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alListener3f
(JNIEnv *, jobject, jint, jfloat, jfloat, jfloat);
/*
* Class: com_jme3_audio_android_AndroidAL
* Method: alSource3i
* Signature: (IIIII)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidAL_alSource3i
(JNIEnv *, jobject, jint, jint, jint, jint, jint);
#ifdef __cplusplus
}
#endif
#endif

@ -1,5 +1,5 @@
#include "util.h" #include "util.h"
#include "com_jme3_audio_android_AndroidALC.h" #include "../headers/com_jme3_audio_android_AndroidALC.h"
#include "AL/alc.h" #include "AL/alc.h"
#include "AL/alext.h" #include "AL/alext.h"

@ -1,77 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jme3_audio_android_AndroidALC */
#ifndef _Included_com_jme3_audio_android_AndroidALC
#define _Included_com_jme3_audio_android_AndroidALC
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: createALC
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_createALC
(JNIEnv *, jobject);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: destroyALC
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_destroyALC
(JNIEnv *, jobject);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: isCreated
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidALC_isCreated
(JNIEnv *, jobject);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: alcGetString
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidALC_alcGetString
(JNIEnv *, jobject, jint);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: alcIsExtensionPresent
* Signature: (Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidALC_alcIsExtensionPresent
(JNIEnv *, jobject, jstring);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: alcGetInteger
* Signature: (ILjava/nio/IntBuffer;I)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcGetInteger
(JNIEnv *, jobject, jint, jobject, jint);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: alcDevicePauseSOFT
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDevicePauseSOFT
(JNIEnv *, jobject);
/*
* Class: com_jme3_audio_android_AndroidALC
* Method: alcDeviceResumeSOFT
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidALC_alcDeviceResumeSOFT
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

@ -1,5 +1,5 @@
#include "util.h" #include "util.h"
#include "com_jme3_audio_android_AndroidEFX.h" #include "../headers/com_jme3_audio_android_AndroidEFX.h"
#include "AL/alext.h" #include "AL/alext.h"
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots

@ -1,101 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jme3_audio_android_AndroidEFX */
#ifndef _Included_com_jme3_audio_android_AndroidEFX
#define _Included_com_jme3_audio_android_AndroidEFX
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alGenAuxiliaryEffectSlots
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenAuxiliaryEffectSlots
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alGenEffects
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenEffects
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alEffecti
* Signature: (III)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffecti
(JNIEnv *, jobject, jint, jint, jint);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alAuxiliaryEffectSloti
* Signature: (III)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alAuxiliaryEffectSloti
(JNIEnv *, jobject, jint, jint, jint);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alDeleteEffects
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteEffects
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alDeleteAuxiliaryEffectSlots
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteAuxiliaryEffectSlots
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alGenFilters
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alGenFilters
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alFilteri
* Signature: (III)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alFilteri
(JNIEnv *, jobject, jint, jint, jint);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alFilterf
* Signature: (IIF)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alFilterf
(JNIEnv *, jobject, jint, jint, jfloat);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alDeleteFilters
* Signature: (ILjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alDeleteFilters
(JNIEnv *, jobject, jint, jobject);
/*
* Class: com_jme3_audio_android_AndroidEFX
* Method: alEffectf
* Signature: (IIF)V
*/
JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidEFX_alEffectf
(JNIEnv *, jobject, jint, jint, jfloat);
#ifdef __cplusplus
}
#endif
#endif

@ -1,3 +1,5 @@
apply plugin: 'java'
if (!hasProperty('mainClass')) { if (!hasProperty('mainClass')) {
ext.mainClass = '' ext.mainClass = ''
} }
@ -7,3 +9,8 @@ dependencies {
compile project(':jme3-plugins') compile project(':jme3-plugins')
compileOnly 'android:android' compileOnly 'android:android'
} }
compileJava {
// The Android-Native Project requires the jni headers to be generated, so we do that here
options.compilerArgs += ["-h", "${project.rootDir}/jme3-android-native/src/native/headers"]
}

@ -3,7 +3,6 @@ package com.jme3.app;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable; import android.graphics.drawable.NinePatchDrawable;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
@ -89,7 +88,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
protected int eglStencilBits = 0; protected int eglStencilBits = 0;
/** /**
* Set the desired frame rate. If frameRate > 0, the application * Set the desired frame rate. If frameRate higher than 0, the application
* will be capped at the desired frame rate. * will be capped at the desired frame rate.
* (default = -1, no frame rate cap) * (default = -1, no frame rate cap)
*/ */
@ -240,9 +239,8 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
// Create application instance // Create application instance
try { try {
if (app == null) { if (app == null) {
@SuppressWarnings("unchecked") Class clazz = Class.forName(appClass);
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass); app = (LegacyApplication)clazz.newInstance();
app = clazz.newInstance();
} }
app.setSettings(settings); app.setSettings(settings);
@ -363,6 +361,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
* @param dialog * @param dialog
* @param whichButton * @param whichButton
*/ */
@Override
public void onClick(DialogInterface dialog, int whichButton) { public void onClick(DialogInterface dialog, int whichButton) {
if (whichButton != -2) { if (whichButton != -2) {
if (app != null) { if (app != null) {
@ -474,6 +473,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
handler.setLevel(Level.ALL); handler.setLevel(Level.ALL);
} }
@Override
public void initialize() { public void initialize() {
app.initialize(); app.initialize();
if (handleExitHook) { if (handleExitHook) {
@ -489,10 +489,12 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
} }
} }
@Override
public void reshape(int width, int height) { public void reshape(int width, int height) {
app.reshape(width, height); app.reshape(width, height);
} }
@Override
public void update() { public void update() {
app.update(); app.update();
// call to remove the splash screen, if present. // call to remove the splash screen, if present.
@ -504,10 +506,12 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
} }
} }
@Override
public void requestClose(boolean esc) { public void requestClose(boolean esc) {
app.requestClose(esc); app.requestClose(esc);
} }
@Override
public void destroy() { public void destroy() {
if (app != null) { if (app != null) {
app.destroy(); app.destroy();
@ -517,6 +521,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
} }
} }
@Override
public void gainFocus() { public void gainFocus() {
logger.fine("gainFocus"); logger.fine("gainFocus");
if (view != null) { if (view != null) {
@ -548,6 +553,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
} }
} }
@Override
public void loseFocus() { public void loseFocus() {
logger.fine("loseFocus"); logger.fine("loseFocus");
if (app != null) { if (app != null) {

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2015 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -116,7 +116,7 @@ public class AndroidHarnessFragment extends Fragment implements
protected int eglStencilBits = 0; protected int eglStencilBits = 0;
/** /**
* Set the desired frame rate. If frameRate > 0, the application * Set the desired frame rate. If frameRate higher than 0, the application
* will be capped at the desired frame rate. * will be capped at the desired frame rate.
* (default = -1, no frame rate cap) * (default = -1, no frame rate cap)
*/ */
@ -125,11 +125,11 @@ public class AndroidHarnessFragment extends Fragment implements
/** /**
* Set the maximum resolution for the surfaceview in either the * Set the maximum resolution for the surfaceview in either the
* width or height screen direction depending on the screen size. * width or height screen direction depending on the screen size.
* If the surfaceview is retangular, the longest side (width or height) * If the surfaceview is rectangular, the longest side (width or height)
* will have the resolution set to a maximum of maxResolutionDimension. * will have the resolution set to a maximum of maxResolutionDimension.
* The other direction will be set to a value that maintains the aspect * The other direction will be set to a value that maintains the aspect
* ratio of the surfaceview. </br> * ratio of the surfaceview. </br>
* Any value < 0 (default = -1) will result in the surfaceview having the * Any value less than 0 (default = -1) will result in the surfaceview having the
* same resolution as the view layout (ie. no max resolution). * same resolution as the view layout (ie. no max resolution).
*/ */
protected int maxResolutionDimension = -1; protected int maxResolutionDimension = -1;
@ -257,9 +257,8 @@ public class AndroidHarnessFragment extends Fragment implements
// Create application instance // Create application instance
try { try {
if (app == null) { if (app == null) {
@SuppressWarnings("unchecked") Class clazz = Class.forName(appClass);
Class<? extends LegacyApplication> clazz = (Class<? extends LegacyApplication>) Class.forName(appClass); app = (LegacyApplication)clazz.newInstance();
app = clazz.newInstance();
} }
app.setSettings(settings); app.setSettings(settings);
@ -276,17 +275,17 @@ public class AndroidHarnessFragment extends Fragment implements
} }
/** /**
* Called by the system to create the View hierchy associated with this * Called by the system to create the View hierarchy associated with this
* Fragment. For jME, this is a FrameLayout that contains the GLSurfaceView * Fragment. For jME, this is a FrameLayout that contains the GLSurfaceView
* and an overlaying SplashScreen Image (if used). The View that is returned * and an overlaying SplashScreen Image (if used). The View that is returned
* will be placed on the screen within the boundries of the View borders defined * will be placed on the screen within the boundaries of the View borders defined
* by the Activity's layout parameters for this Fragment. For jME, we also * by the Activity's layout parameters for this Fragment. For jME, we also
* update the application reference to the new view. * update the application reference to the new view.
* *
* @param inflater * @param inflater
* @param container * @param container
* @param savedInstanceState * @param savedInstanceState
* @return * @return the new view
*/ */
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -684,10 +683,10 @@ public class AndroidHarnessFragment extends Fragment implements
if (viewWidth > viewHeight && viewWidth > maxResolutionDimension) { if (viewWidth > viewHeight && viewWidth > maxResolutionDimension) {
// landscape // landscape
fixedSizeWidth = maxResolutionDimension; fixedSizeWidth = maxResolutionDimension;
fixedSizeHeight = (int)(maxResolutionDimension * ((float)viewHeight / (float)viewWidth)); fixedSizeHeight = (int)(maxResolutionDimension * (viewHeight / (float)viewWidth));
} else if (viewHeight > viewWidth && viewHeight > maxResolutionDimension) { } else if (viewHeight > viewWidth && viewHeight > maxResolutionDimension) {
// portrait // portrait
fixedSizeWidth = (int)(maxResolutionDimension * ((float)viewWidth / (float)viewHeight)); fixedSizeWidth = (int)(maxResolutionDimension * (viewWidth / (float)viewHeight));
fixedSizeHeight = maxResolutionDimension; fixedSizeHeight = maxResolutionDimension;
} else if (viewWidth == viewHeight && viewWidth > maxResolutionDimension) { } else if (viewWidth == viewHeight && viewWidth > maxResolutionDimension) {
fixedSizeWidth = maxResolutionDimension; fixedSizeWidth = maxResolutionDimension;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2014 jMonkeyEngine * Copyright (c) 2014-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -63,7 +63,7 @@ import com.jme3.renderer.queue.RenderQueue;
* *
* <p>This profiler uses the Android Trace class which is only supported * <p>This profiler uses the Android Trace class which is only supported
* on Android SDK rev 18 and higher (ver 4.3 and higher). If the * on Android SDK rev 18 and higher (ver 4.3 and higher). If the
* device is running a version < rev 18, the logging will * device is running a version less than rev 18, the logging will
* be skipped.</p> * be skipped.</p>
* *
* <p>In the MainActivity class, add the following:</p> * <p>In the MainActivity class, add the following:</p>
@ -82,6 +82,7 @@ import com.jme3.renderer.queue.RenderQueue;
public class DefaultAndroidProfiler implements AppProfiler { public class DefaultAndroidProfiler implements AppProfiler {
private int androidApiLevel = Build.VERSION.SDK_INT; private int androidApiLevel = Build.VERSION.SDK_INT;
@Override
public void appStep(AppStep appStep) { public void appStep(AppStep appStep) {
if (androidApiLevel >= 18) { if (androidApiLevel >= 18) {
switch(appStep) { switch(appStep) {
@ -135,6 +136,12 @@ public class DefaultAndroidProfiler implements AppProfiler {
} }
} }
@Override
public void appSubStep(String... additionalInfo) {
}
@Override
public void vpStep(VpStep vpStep, ViewPort vp, RenderQueue.Bucket bucket) { public void vpStep(VpStep vpStep, ViewPort vp, RenderQueue.Bucket bucket) {
if (androidApiLevel >= 18) { if (androidApiLevel >= 18) {
switch (vpStep) { switch (vpStep) {

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -478,7 +478,7 @@ public class MjpegFileWriter {
baos.write(fcc); baos.write(fcc);
baos.write(intBytes(swapInt(cb))); baos.write(intBytes(swapInt(cb)));
for (int i = 0; i < ind.size(); i++) { for (int i = 0; i < ind.size(); i++) {
AVIIndex in = (AVIIndex) ind.get(i); AVIIndex in = ind.get(i);
baos.write(in.toBytes()); baos.write(in.toBytes());
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -73,6 +73,7 @@ public class VideoRecorderAppState extends AbstractAppState {
private Application app; private Application app;
private ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() { private ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
Thread th = new Thread(r); Thread th = new Thread(r);
th.setName("jME3 Video Processor"); th.setName("jME3 Video Processor");
@ -123,7 +124,6 @@ public class VideoRecorderAppState extends AbstractAppState {
* This constructor allows you to specify the output file of the video as well as the quality * This constructor allows you to specify the output file of the video as well as the quality
* @param file the video file * @param file the video file
* @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file) * @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
* @param framerate the frame rate of the resulting video, the application will be locked to this framerate
*/ */
public VideoRecorderAppState(File file, float quality) { public VideoRecorderAppState(File file, float quality) {
this.file = file; this.file = file;
@ -240,6 +240,7 @@ public class VideoRecorderAppState extends AbstractAppState {
renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8); renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8);
executor.submit(new Callable<Void>() { executor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception { public Void call() throws Exception {
if (fastMode) { if (fastMode) {
item.data = item.buffer.array(); item.data = item.buffer.array();
@ -261,6 +262,7 @@ public class VideoRecorderAppState extends AbstractAppState {
} }
} }
@Override
public void initialize(RenderManager rm, ViewPort viewPort) { public void initialize(RenderManager rm, ViewPort viewPort) {
logger.log(Level.INFO, "initialize in VideoProcessor"); logger.log(Level.INFO, "initialize in VideoProcessor");
this.camera = viewPort.getCamera(); this.camera = viewPort.getCamera();
@ -276,13 +278,16 @@ public class VideoRecorderAppState extends AbstractAppState {
} }
} }
@Override
public void reshape(ViewPort vp, int w, int h) { public void reshape(ViewPort vp, int w, int h) {
} }
@Override
public boolean isInitialized() { public boolean isInitialized() {
return this.isInitilized; return this.isInitilized;
} }
@Override
public void preFrame(float tpf) { public void preFrame(float tpf) {
if (null == writer) { if (null == writer) {
try { try {
@ -293,14 +298,17 @@ public class VideoRecorderAppState extends AbstractAppState {
} }
} }
@Override
public void postQueue(RenderQueue rq) { public void postQueue(RenderQueue rq) {
} }
@Override
public void postFrame(FrameBuffer out) { public void postFrame(FrameBuffer out) {
numFrames++; numFrames++;
addImage(renderManager.getRenderer(), out); addImage(renderManager.getRenderer(), out);
} }
@Override
public void cleanup() { public void cleanup() {
logger.log(Level.INFO, "cleanup in VideoProcessor"); logger.log(Level.INFO, "cleanup in VideoProcessor");
logger.log(Level.INFO, "VideoProcessor numFrames: {0}", numFrames); logger.log(Level.INFO, "VideoProcessor numFrames: {0}", numFrames);
@ -333,22 +341,27 @@ public class VideoRecorderAppState extends AbstractAppState {
this.ticks = 0; this.ticks = 0;
} }
@Override
public long getTime() { public long getTime() {
return (long) (this.ticks * (1.0f / this.framerate) * 1000f); return (long) (this.ticks * (1.0f / this.framerate) * 1000f);
} }
@Override
public long getResolution() { public long getResolution() {
return 1000L; return 1000L;
} }
@Override
public float getFrameRate() { public float getFrameRate() {
return this.framerate; return this.framerate;
} }
@Override
public float getTimePerFrame() { public float getTimePerFrame() {
return (float) (1.0f / this.framerate); return 1.0f / this.framerate;
} }
@Override
public void update() { public void update() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
long difference = time - lastTime; long difference = time - lastTime;
@ -365,6 +378,7 @@ public class VideoRecorderAppState extends AbstractAppState {
this.ticks++; this.ticks++;
} }
@Override
public void reset() { public void reset() {
this.ticks = 0; this.ticks = 0;
} }

@ -6,7 +6,6 @@ import com.jme3.asset.*;
import com.jme3.system.android.JmeAndroidSystem; import com.jme3.system.android.JmeAndroidSystem;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class AndroidLocator implements AssetLocator { public class AndroidLocator implements AssetLocator {
@ -18,6 +17,7 @@ public class AndroidLocator implements AssetLocator {
public AndroidLocator() { public AndroidLocator() {
} }
@Override
public void setRootPath(String rootPath) { public void setRootPath(String rootPath) {
this.rootPath = rootPath; this.rootPath = rootPath;
} }

@ -10,44 +10,64 @@ public final class AndroidAL implements AL {
public AndroidAL() { public AndroidAL() {
} }
@Override
public native String alGetString(int parameter); public native String alGetString(int parameter);
@Override
public native int alGenSources(); public native int alGenSources();
@Override
public native int alGetError(); public native int alGetError();
@Override
public native void alDeleteSources(int numSources, IntBuffer sources); public native void alDeleteSources(int numSources, IntBuffer sources);
@Override
public native void alGenBuffers(int numBuffers, IntBuffer buffers); public native void alGenBuffers(int numBuffers, IntBuffer buffers);
@Override
public native void alDeleteBuffers(int numBuffers, IntBuffer buffers); public native void alDeleteBuffers(int numBuffers, IntBuffer buffers);
@Override
public native void alSourceStop(int source); public native void alSourceStop(int source);
@Override
public native void alSourcei(int source, int param, int value); public native void alSourcei(int source, int param, int value);
@Override
public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency); public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);
@Override
public native void alSourcePlay(int source); public native void alSourcePlay(int source);
@Override
public native void alSourcePause(int source); public native void alSourcePause(int source);
@Override
public native void alSourcef(int source, int param, float value); public native void alSourcef(int source, int param, float value);
@Override
public native void alSource3f(int source, int param, float value1, float value2, float value3); public native void alSource3f(int source, int param, float value1, float value2, float value3);
@Override
public native int alGetSourcei(int source, int param); public native int alGetSourcei(int source, int param);
@Override
public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers); public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);
@Override
public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers); public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);
@Override
public native void alListener(int param, FloatBuffer data); public native void alListener(int param, FloatBuffer data);
@Override
public native void alListenerf(int param, float value); public native void alListenerf(int param, float value);
@Override
public native void alListener3f(int param, float value1, float value2, float value3); public native void alListener3f(int param, float value1, float value2, float value3);
@Override
public native void alSource3i(int source, int param, int value1, int value2, int value3); public native void alSource3i(int source, int param, int value1, int value2, int value3);
} }

@ -12,19 +12,27 @@ public final class AndroidALC implements ALC {
public AndroidALC() { public AndroidALC() {
} }
@Override
public native void createALC(); public native void createALC();
@Override
public native void destroyALC(); public native void destroyALC();
@Override
public native boolean isCreated(); public native boolean isCreated();
@Override
public native String alcGetString(int parameter); public native String alcGetString(int parameter);
@Override
public native boolean alcIsExtensionPresent(String extension); public native boolean alcIsExtensionPresent(String extension);
@Override
public native void alcGetInteger(int param, IntBuffer buffer, int size); public native void alcGetInteger(int param, IntBuffer buffer, int size);
@Override
public native void alcDevicePauseSOFT(); public native void alcDevicePauseSOFT();
@Override
public native void alcDeviceResumeSOFT(); public native void alcDeviceResumeSOFT();
} }

@ -8,25 +8,36 @@ public class AndroidEFX implements EFX {
public AndroidEFX() { public AndroidEFX() {
} }
@Override
public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers); public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers);
@Override
public native void alGenEffects(int numEffects, IntBuffer buffers); public native void alGenEffects(int numEffects, IntBuffer buffers);
@Override
public native void alEffecti(int effect, int param, int value); public native void alEffecti(int effect, int param, int value);
@Override
public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value); public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value);
@Override
public native void alDeleteEffects(int numEffects, IntBuffer buffers); public native void alDeleteEffects(int numEffects, IntBuffer buffers);
@Override
public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers); public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers);
@Override
public native void alGenFilters(int numFilters, IntBuffer buffers); public native void alGenFilters(int numFilters, IntBuffer buffers);
@Override
public native void alFilteri(int filter, int param, int value); public native void alFilteri(int filter, int param, int value);
@Override
public native void alFilterf(int filter, int param, float value); public native void alFilterf(int filter, int param, float value);
@Override
public native void alDeleteFilters(int numFilters, IntBuffer buffers); public native void alDeleteFilters(int numFilters, IntBuffer buffers);
@Override
public native void alEffectf(int effect, int param, float value); public native void alEffectf(int effect, int param, float value);
} }

@ -46,6 +46,7 @@ public class NativeVorbisLoader implements AssetLoader {
throw new IOException("Not supported for audio streams"); throw new IOException("Not supported for audio streams");
} }
@Override
public void setTime(float time) { public void setTime(float time) {
try { try {
file.seekTime(time); file.seekTime(time);

@ -36,7 +36,6 @@ import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import com.jme3.input.event.TouchEvent; import com.jme3.input.event.TouchEvent;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**

@ -42,7 +42,6 @@ import android.view.View;
import com.jme3.input.JoyInput; import com.jme3.input.JoyInput;
import com.jme3.input.TouchInput; import com.jme3.input.TouchInput;
import com.jme3.system.AppSettings; import com.jme3.system.AppSettings;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**

@ -37,7 +37,6 @@ import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**

@ -71,10 +71,11 @@ import java.util.logging.Logger;
* This is done to allow for battery conservation when sensor data or gamepads * This is done to allow for battery conservation when sensor data or gamepads
* are not required by the application. * are not required by the application.
* *
* {@code
* To use the joystick rumble feature, the following line needs to be * To use the joystick rumble feature, the following line needs to be
* added to the Android Manifest File * added to the Android Manifest File
* <uses-permission android:name="android.permission.VIBRATE"/> * <uses-permission android:name="android.permission.VIBRATE"/>
* * }
* @author iwgeric * @author iwgeric
*/ */
public class AndroidJoyInput implements JoyInput { public class AndroidJoyInput implements JoyInput {

@ -113,7 +113,7 @@ public class AndroidJoystickJoyInput14 {
joysticks.clear(); joysticks.clear();
joystickIndex.clear(); joystickIndex.clear();
ArrayList gameControllerDeviceIds = new ArrayList(); ArrayList<Integer> gameControllerDeviceIds = new ArrayList<>();
int[] deviceIds = InputDevice.getDeviceIds(); int[] deviceIds = InputDevice.getDeviceIds();
for (int deviceId : deviceIds) { for (int deviceId : deviceIds) {
InputDevice dev = InputDevice.getDevice(deviceId); InputDevice dev = InputDevice.getDevice(deviceId);

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2018 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -561,7 +561,7 @@ public class AndroidSensorJoyInput implements SensorEventListener {
} }
} }
} }
} else if (sensorData != null) { } else {
if (!sensorData.haveData) { if (!sensorData.haveData) {
sensorData.haveData = true; sensorData.haveData = true;
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -131,8 +131,8 @@ public class AndroidTouchInput implements TouchInput {
// view width and height are 0 until the view is displayed on the screen // view width and height are 0 until the view is displayed on the screen
if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) { if (androidInput.getView().getWidth() != 0 && androidInput.getView().getHeight() != 0) {
scaleX = (float)settings.getWidth() / (float)androidInput.getView().getWidth(); scaleX = settings.getWidth() / (float)androidInput.getView().getWidth();
scaleY = (float)settings.getHeight() / (float)androidInput.getView().getHeight(); scaleY = settings.getHeight() / (float)androidInput.getView().getHeight();
} }
logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}", logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}",
new Object[]{scaleX, scaleY}); new Object[]{scaleX, scaleY});

@ -36,7 +36,6 @@ import android.view.MotionEvent;
import com.jme3.input.event.TouchEvent; import com.jme3.input.event.TouchEvent;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import java.util.HashMap; import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -87,7 +87,7 @@ public class TouchEventPool {
TouchEvent evt = null; TouchEvent evt = null;
int curSize = eventPool.size(); int curSize = eventPool.size();
while (curSize > 0) { while (curSize > 0) {
evt = (TouchEvent)eventPool.pop(); evt = eventPool.pop();
if (evt.isConsumed()) { if (evt.isConsumed()) {
break; break;
} else { } else {

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2015 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -34,6 +34,7 @@ package com.jme3.renderer.android;
import android.opengl.*; import android.opengl.*;
import com.jme3.renderer.RendererException; import com.jme3.renderer.RendererException;
import com.jme3.renderer.opengl.*; import com.jme3.renderer.opengl.*;
import com.jme3.util.BufferUtils;
import java.nio.Buffer; import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -41,8 +42,11 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
public class AndroidGL implements GL, GLExt, GLFbo { public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
IntBuffer tmpBuff = BufferUtils.createIntBuffer(1);
@Override
public void resetStats() { public void resetStats() {
} }
@ -83,10 +87,12 @@ public class AndroidGL implements GL, GLExt, GLFbo {
} }
} }
@Override
public void glActiveTexture(int texture) { public void glActiveTexture(int texture) {
GLES20.glActiveTexture(texture); GLES20.glActiveTexture(texture);
} }
@Override
public void glAttachShader(int program, int shader) { public void glAttachShader(int program, int shader) {
GLES20.glAttachShader(program, shader); GLES20.glAttachShader(program, shader);
} }
@ -96,144 +102,179 @@ public class AndroidGL implements GL, GLExt, GLFbo {
GLES30.glBeginQuery(target, query); GLES30.glBeginQuery(target, query);
} }
@Override
public void glBindBuffer(int target, int buffer) { public void glBindBuffer(int target, int buffer) {
GLES20.glBindBuffer(target, buffer); GLES20.glBindBuffer(target, buffer);
} }
@Override
public void glBindTexture(int target, int texture) { public void glBindTexture(int target, int texture) {
GLES20.glBindTexture(target, texture); GLES20.glBindTexture(target, texture);
} }
@Override
public void glBlendFunc(int sfactor, int dfactor) { public void glBlendFunc(int sfactor, int dfactor) {
GLES20.glBlendFunc(sfactor, dfactor); GLES20.glBlendFunc(sfactor, dfactor);
} }
@Override
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) { public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) {
GLES20.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); GLES20.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
} }
@Override
public void glBufferData(int target, FloatBuffer data, int usage) { public void glBufferData(int target, FloatBuffer data, int usage) {
GLES20.glBufferData(target, getLimitBytes(data), data, usage); GLES20.glBufferData(target, getLimitBytes(data), data, usage);
} }
@Override
public void glBufferData(int target, ShortBuffer data, int usage) { public void glBufferData(int target, ShortBuffer data, int usage) {
GLES20.glBufferData(target, getLimitBytes(data), data, usage); GLES20.glBufferData(target, getLimitBytes(data), data, usage);
} }
@Override
public void glBufferData(int target, ByteBuffer data, int usage) { public void glBufferData(int target, ByteBuffer data, int usage) {
GLES20.glBufferData(target, getLimitBytes(data), data, usage); GLES20.glBufferData(target, getLimitBytes(data), data, usage);
} }
@Override
public void glBufferData(int target, long data_size, int usage) { public void glBufferData(int target, long data_size, int usage) {
GLES20.glBufferData(target, (int) data_size, null, usage); GLES20.glBufferData(target, (int) data_size, null, usage);
} }
@Override
public void glBufferSubData(int target, long offset, FloatBuffer data) { public void glBufferSubData(int target, long offset, FloatBuffer data) {
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data); GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
} }
@Override
public void glBufferSubData(int target, long offset, ShortBuffer data) { public void glBufferSubData(int target, long offset, ShortBuffer data) {
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data); GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
} }
@Override
public void glBufferSubData(int target, long offset, ByteBuffer data) { public void glBufferSubData(int target, long offset, ByteBuffer data) {
GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data); GLES20.glBufferSubData(target, (int) offset, getLimitBytes(data), data);
} }
@Override
public void glGetBufferSubData(int target, long offset, ByteBuffer data) { public void glGetBufferSubData(int target, long offset, ByteBuffer data) {
throw new UnsupportedOperationException("OpenGL ES 2 does not support glGetBufferSubData"); throw new UnsupportedOperationException("OpenGL ES 2 does not support glGetBufferSubData");
} }
@Override
public void glClear(int mask) { public void glClear(int mask) {
GLES20.glClear(mask); GLES20.glClear(mask);
} }
@Override
public void glClearColor(float red, float green, float blue, float alpha) { public void glClearColor(float red, float green, float blue, float alpha) {
GLES20.glClearColor(red, green, blue, alpha); GLES20.glClearColor(red, green, blue, alpha);
} }
@Override
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) { public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
GLES20.glColorMask(red, green, blue, alpha); GLES20.glColorMask(red, green, blue, alpha);
} }
@Override
public void glCompileShader(int shader) { public void glCompileShader(int shader) {
GLES20.glCompileShader(shader); GLES20.glCompileShader(shader);
} }
@Override
public void glCompressedTexImage2D(int target, int level, int internalformat, int width, int height, int border, ByteBuffer data) { public void glCompressedTexImage2D(int target, int level, int internalformat, int width, int height, int border, ByteBuffer data) {
GLES20.glCompressedTexImage2D(target, level, internalformat, width, height, 0, getLimitBytes(data), data); GLES20.glCompressedTexImage2D(target, level, internalformat, width, height, 0, getLimitBytes(data), data);
} }
@Override
public void glCompressedTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, ByteBuffer data) { public void glCompressedTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, ByteBuffer data) {
GLES20.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, getLimitBytes(data), data); GLES20.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, getLimitBytes(data), data);
} }
@Override
public int glCreateProgram() { public int glCreateProgram() {
return GLES20.glCreateProgram(); return GLES20.glCreateProgram();
} }
@Override
public int glCreateShader(int shaderType) { public int glCreateShader(int shaderType) {
return GLES20.glCreateShader(shaderType); return GLES20.glCreateShader(shaderType);
} }
@Override
public void glCullFace(int mode) { public void glCullFace(int mode) {
GLES20.glCullFace(mode); GLES20.glCullFace(mode);
} }
@Override
public void glDeleteBuffers(IntBuffer buffers) { public void glDeleteBuffers(IntBuffer buffers) {
checkLimit(buffers); checkLimit(buffers);
GLES20.glDeleteBuffers(buffers.limit(), buffers); GLES20.glDeleteBuffers(buffers.limit(), buffers);
} }
@Override
public void glDeleteProgram(int program) { public void glDeleteProgram(int program) {
GLES20.glDeleteProgram(program); GLES20.glDeleteProgram(program);
} }
@Override
public void glDeleteShader(int shader) { public void glDeleteShader(int shader) {
GLES20.glDeleteShader(shader); GLES20.glDeleteShader(shader);
} }
@Override
public void glDeleteTextures(IntBuffer textures) { public void glDeleteTextures(IntBuffer textures) {
checkLimit(textures); checkLimit(textures);
GLES20.glDeleteTextures(textures.limit(), textures); GLES20.glDeleteTextures(textures.limit(), textures);
} }
@Override
public void glDepthFunc(int func) { public void glDepthFunc(int func) {
GLES20.glDepthFunc(func); GLES20.glDepthFunc(func);
} }
@Override
public void glDepthMask(boolean flag) { public void glDepthMask(boolean flag) {
GLES20.glDepthMask(flag); GLES20.glDepthMask(flag);
} }
@Override
public void glDepthRange(double nearVal, double farVal) { public void glDepthRange(double nearVal, double farVal) {
GLES20.glDepthRangef((float)nearVal, (float)farVal); GLES20.glDepthRangef((float)nearVal, (float)farVal);
} }
@Override
public void glDetachShader(int program, int shader) { public void glDetachShader(int program, int shader) {
GLES20.glDetachShader(program, shader); GLES20.glDetachShader(program, shader);
} }
@Override
public void glDisable(int cap) { public void glDisable(int cap) {
GLES20.glDisable(cap); GLES20.glDisable(cap);
} }
@Override
public void glDisableVertexAttribArray(int index) { public void glDisableVertexAttribArray(int index) {
GLES20.glDisableVertexAttribArray(index); GLES20.glDisableVertexAttribArray(index);
} }
@Override
public void glDrawArrays(int mode, int first, int count) { public void glDrawArrays(int mode, int first, int count) {
GLES20.glDrawArrays(mode, first, count); GLES20.glDrawArrays(mode, first, count);
} }
@Override
public void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) { public void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) {
GLES20.glDrawElements(mode, count, type, (int)indices); GLES20.glDrawElements(mode, count, type, (int)indices);
} }
@Override
public void glEnable(int cap) { public void glEnable(int cap) {
GLES20.glEnable(cap); GLES20.glEnable(cap);
} }
@Override
public void glEnableVertexAttribArray(int index) { public void glEnableVertexAttribArray(int index) {
GLES20.glEnableVertexAttribArray(index); GLES20.glEnableVertexAttribArray(index);
} }
@ -243,11 +284,13 @@ public class AndroidGL implements GL, GLExt, GLFbo {
GLES30.glEndQuery(target); GLES30.glEndQuery(target);
} }
@Override
public void glGenBuffers(IntBuffer buffers) { public void glGenBuffers(IntBuffer buffers) {
checkLimit(buffers); checkLimit(buffers);
GLES20.glGenBuffers(buffers.limit(), buffers); GLES20.glGenBuffers(buffers.limit(), buffers);
} }
@Override
public void glGenTextures(IntBuffer textures) { public void glGenTextures(IntBuffer textures) {
checkLimit(textures); checkLimit(textures);
GLES20.glGenTextures(textures.limit(), textures); GLES20.glGenTextures(textures.limit(), textures);
@ -258,29 +301,35 @@ public class AndroidGL implements GL, GLExt, GLFbo {
GLES30.glGenQueries(num, buff); GLES30.glGenQueries(num, buff);
} }
@Override
public int glGetAttribLocation(int program, String name) { public int glGetAttribLocation(int program, String name) {
return GLES20.glGetAttribLocation(program, name); return GLES20.glGetAttribLocation(program, name);
} }
@Override
public void glGetBoolean(int pname, ByteBuffer params) { public void glGetBoolean(int pname, ByteBuffer params) {
// GLES20.glGetBoolean(pname, params); // GLES20.glGetBoolean(pname, params);
throw new UnsupportedOperationException("Today is not a good day for this"); throw new UnsupportedOperationException("Today is not a good day for this");
} }
@Override
public int glGetError() { public int glGetError() {
return GLES20.glGetError(); return GLES20.glGetError();
} }
@Override
public void glGetInteger(int pname, IntBuffer params) { public void glGetInteger(int pname, IntBuffer params) {
checkLimit(params); checkLimit(params);
GLES20.glGetIntegerv(pname, params); GLES20.glGetIntegerv(pname, params);
} }
@Override
public void glGetProgram(int program, int pname, IntBuffer params) { public void glGetProgram(int program, int pname, IntBuffer params) {
checkLimit(params); checkLimit(params);
GLES20.glGetProgramiv(program, pname, params); GLES20.glGetProgramiv(program, pname, params);
} }
@Override
public String glGetProgramInfoLog(int program, int maxLength) { public String glGetProgramInfoLog(int program, int maxLength) {
return GLES20.glGetProgramInfoLog(program); return GLES20.glGetProgramInfoLog(program);
} }
@ -300,51 +349,63 @@ public class AndroidGL implements GL, GLExt, GLFbo {
return buff.get(0); return buff.get(0);
} }
@Override
public void glGetShader(int shader, int pname, IntBuffer params) { public void glGetShader(int shader, int pname, IntBuffer params) {
checkLimit(params); checkLimit(params);
GLES20.glGetShaderiv(shader, pname, params); GLES20.glGetShaderiv(shader, pname, params);
} }
@Override
public String glGetShaderInfoLog(int shader, int maxLength) { public String glGetShaderInfoLog(int shader, int maxLength) {
return GLES20.glGetShaderInfoLog(shader); return GLES20.glGetShaderInfoLog(shader);
} }
@Override
public String glGetString(int name) { public String glGetString(int name) {
return GLES20.glGetString(name); return GLES20.glGetString(name);
} }
@Override
public int glGetUniformLocation(int program, String name) { public int glGetUniformLocation(int program, String name) {
return GLES20.glGetUniformLocation(program, name); return GLES20.glGetUniformLocation(program, name);
} }
@Override
public boolean glIsEnabled(int cap) { public boolean glIsEnabled(int cap) {
return GLES20.glIsEnabled(cap); return GLES20.glIsEnabled(cap);
} }
@Override
public void glLineWidth(float width) { public void glLineWidth(float width) {
GLES20.glLineWidth(width); GLES20.glLineWidth(width);
} }
@Override
public void glLinkProgram(int program) { public void glLinkProgram(int program) {
GLES20.glLinkProgram(program); GLES20.glLinkProgram(program);
} }
@Override
public void glPixelStorei(int pname, int param) { public void glPixelStorei(int pname, int param) {
GLES20.glPixelStorei(pname, param); GLES20.glPixelStorei(pname, param);
} }
@Override
public void glPolygonOffset(float factor, float units) { public void glPolygonOffset(float factor, float units) {
GLES20.glPolygonOffset(factor, units); GLES20.glPolygonOffset(factor, units);
} }
@Override
public void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) { public void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) {
GLES20.glReadPixels(x, y, width, height, format, type, data); GLES20.glReadPixels(x, y, width, height, format, type, data);
} }
@Override
public void glScissor(int x, int y, int width, int height) { public void glScissor(int x, int y, int width, int height) {
GLES20.glScissor(x, y, width, height); GLES20.glScissor(x, y, width, height);
} }
@Override
public void glShaderSource(int shader, String[] string, IntBuffer length) { public void glShaderSource(int shader, String[] string, IntBuffer length) {
if (string.length != 1) { if (string.length != 1) {
throw new UnsupportedOperationException("Today is not a good day"); throw new UnsupportedOperationException("Today is not a good day");
@ -352,186 +413,231 @@ public class AndroidGL implements GL, GLExt, GLFbo {
GLES20.glShaderSource(shader, string[0]); GLES20.glShaderSource(shader, string[0]);
} }
@Override
public void glStencilFuncSeparate(int face, int func, int ref, int mask) { public void glStencilFuncSeparate(int face, int func, int ref, int mask) {
GLES20.glStencilFuncSeparate(face, func, ref, mask); GLES20.glStencilFuncSeparate(face, func, ref, mask);
} }
@Override
public void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) { public void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
GLES20.glStencilOpSeparate(face, sfail, dpfail, dppass); GLES20.glStencilOpSeparate(face, sfail, dpfail, dppass);
} }
@Override
public void glTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer data) { public void glTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer data) {
GLES20.glTexImage2D(target, level, format, width, height, 0, format, type, data); GLES20.glTexImage2D(target, level, internalFormat, width, height, 0, format, type, data);
} }
@Override
public void glTexParameterf(int target, int pname, float param) { public void glTexParameterf(int target, int pname, float param) {
GLES20.glTexParameterf(target, pname, param); GLES20.glTexParameterf(target, pname, param);
} }
@Override
public void glTexParameteri(int target, int pname, int param) { public void glTexParameteri(int target, int pname, int param) {
GLES20.glTexParameteri(target, pname, param); GLES20.glTexParameteri(target, pname, param);
} }
@Override
public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) { public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) {
GLES20.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data); GLES20.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data);
} }
@Override
public void glUniform1(int location, FloatBuffer value) { public void glUniform1(int location, FloatBuffer value) {
GLES20.glUniform1fv(location, getLimitCount(value, 1), value); GLES20.glUniform1fv(location, getLimitCount(value, 1), value);
} }
@Override
public void glUniform1(int location, IntBuffer value) { public void glUniform1(int location, IntBuffer value) {
GLES20.glUniform1iv(location, getLimitCount(value, 1), value); GLES20.glUniform1iv(location, getLimitCount(value, 1), value);
} }
@Override
public void glUniform1f(int location, float v0) { public void glUniform1f(int location, float v0) {
GLES20.glUniform1f(location, v0); GLES20.glUniform1f(location, v0);
} }
@Override
public void glUniform1i(int location, int v0) { public void glUniform1i(int location, int v0) {
GLES20.glUniform1i(location, v0); GLES20.glUniform1i(location, v0);
} }
@Override
public void glUniform2(int location, IntBuffer value) { public void glUniform2(int location, IntBuffer value) {
GLES20.glUniform2iv(location, getLimitCount(value, 2), value); GLES20.glUniform2iv(location, getLimitCount(value, 2), value);
} }
@Override
public void glUniform2(int location, FloatBuffer value) { public void glUniform2(int location, FloatBuffer value) {
GLES20.glUniform2fv(location, getLimitCount(value, 2), value); GLES20.glUniform2fv(location, getLimitCount(value, 2), value);
} }
@Override
public void glUniform2f(int location, float v0, float v1) { public void glUniform2f(int location, float v0, float v1) {
GLES20.glUniform2f(location, v0, v1); GLES20.glUniform2f(location, v0, v1);
} }
@Override
public void glUniform3(int location, IntBuffer value) { public void glUniform3(int location, IntBuffer value) {
GLES20.glUniform3iv(location, getLimitCount(value, 3), value); GLES20.glUniform3iv(location, getLimitCount(value, 3), value);
} }
@Override
public void glUniform3(int location, FloatBuffer value) { public void glUniform3(int location, FloatBuffer value) {
GLES20.glUniform3fv(location, getLimitCount(value, 3), value); GLES20.glUniform3fv(location, getLimitCount(value, 3), value);
} }
@Override
public void glUniform3f(int location, float v0, float v1, float v2) { public void glUniform3f(int location, float v0, float v1, float v2) {
GLES20.glUniform3f(location, v0, v1, v2); GLES20.glUniform3f(location, v0, v1, v2);
} }
@Override
public void glUniform4(int location, FloatBuffer value) { public void glUniform4(int location, FloatBuffer value) {
GLES20.glUniform4fv(location, getLimitCount(value, 4), value); GLES20.glUniform4fv(location, getLimitCount(value, 4), value);
} }
@Override
public void glUniform4(int location, IntBuffer value) { public void glUniform4(int location, IntBuffer value) {
GLES20.glUniform4iv(location, getLimitCount(value, 4), value); GLES20.glUniform4iv(location, getLimitCount(value, 4), value);
} }
@Override
public void glUniform4f(int location, float v0, float v1, float v2, float v3) { public void glUniform4f(int location, float v0, float v1, float v2, float v3) {
GLES20.glUniform4f(location, v0, v1, v2, v3); GLES20.glUniform4f(location, v0, v1, v2, v3);
} }
@Override
public void glUniformMatrix3(int location, boolean transpose, FloatBuffer value) { public void glUniformMatrix3(int location, boolean transpose, FloatBuffer value) {
GLES20.glUniformMatrix3fv(location, getLimitCount(value, 3 * 3), transpose, value); GLES20.glUniformMatrix3fv(location, getLimitCount(value, 3 * 3), transpose, value);
} }
@Override
public void glUniformMatrix4(int location, boolean transpose, FloatBuffer value) { public void glUniformMatrix4(int location, boolean transpose, FloatBuffer value) {
GLES20.glUniformMatrix4fv(location, getLimitCount(value, 4 * 4), transpose, value); GLES20.glUniformMatrix4fv(location, getLimitCount(value, 4 * 4), transpose, value);
} }
@Override
public void glUseProgram(int program) { public void glUseProgram(int program) {
GLES20.glUseProgram(program); GLES20.glUseProgram(program);
} }
@Override
public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) { public void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
GLES20.glVertexAttribPointer(index, size, type, normalized, stride, (int)pointer); GLES20.glVertexAttribPointer(index, size, type, normalized, stride, (int)pointer);
} }
@Override
public void glViewport(int x, int y, int width, int height) { public void glViewport(int x, int y, int width, int height) {
GLES20.glViewport(x, y, width, height); GLES20.glViewport(x, y, width, height);
} }
@Override
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
throw new UnsupportedOperationException("FBO blit not available on Android"); GLES30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
} }
@Override
public void glBufferData(int target, IntBuffer data, int usage) { public void glBufferData(int target, IntBuffer data, int usage) {
GLES20.glBufferData(target, getLimitBytes(data), data, usage); GLES20.glBufferData(target, getLimitBytes(data), data, usage);
} }
@Override
public void glBufferSubData(int target, long offset, IntBuffer data) { public void glBufferSubData(int target, long offset, IntBuffer data) {
GLES20.glBufferSubData(target, (int)offset, getLimitBytes(data), data); GLES20.glBufferSubData(target, (int)offset, getLimitBytes(data), data);
} }
@Override
public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) { public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
throw new UnsupportedOperationException("Instancing not available on Android"); GLES30.glDrawArraysInstanced(mode, first, count, primcount);
} }
@Override
public void glDrawBuffers(IntBuffer bufs) { public void glDrawBuffers(IntBuffer bufs) {
throw new UnsupportedOperationException("MRT not available on Android"); GLES30.glDrawBuffers(bufs.limit(), bufs);
} }
@Override
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) { public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
throw new UnsupportedOperationException("Instancing not available on Android"); GLES30.glDrawElementsInstanced(mode, indices_count, type, (int)indices_buffer_offset, primcount);
} }
@Override
public void glGetMultisample(int pname, int index, FloatBuffer val) { public void glGetMultisample(int pname, int index, FloatBuffer val) {
throw new UnsupportedOperationException("Multisample renderbuffers not available on Android"); GLES31.glGetMultisamplefv(pname, index, val);
} }
@Override
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) { public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
throw new UnsupportedOperationException("Multisample renderbuffers not available on Android"); GLES30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
} }
@Override
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) { public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
throw new UnsupportedOperationException("Multisample textures not available on Android"); GLES31.glTexStorage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
} }
@Override
public void glVertexAttribDivisorARB(int index, int divisor) { public void glVertexAttribDivisorARB(int index, int divisor) {
throw new UnsupportedOperationException("Instancing not available on Android"); GLES30.glVertexAttribDivisor(index, divisor);
} }
@Override
public void glBindFramebufferEXT(int param1, int param2) { public void glBindFramebufferEXT(int param1, int param2) {
GLES20.glBindFramebuffer(param1, param2); GLES20.glBindFramebuffer(param1, param2);
} }
@Override
public void glBindRenderbufferEXT(int param1, int param2) { public void glBindRenderbufferEXT(int param1, int param2) {
GLES20.glBindRenderbuffer(param1, param2); GLES20.glBindRenderbuffer(param1, param2);
} }
@Override
public int glCheckFramebufferStatusEXT(int param1) { public int glCheckFramebufferStatusEXT(int param1) {
return GLES20.glCheckFramebufferStatus(param1); return GLES20.glCheckFramebufferStatus(param1);
} }
@Override
public void glDeleteFramebuffersEXT(IntBuffer param1) { public void glDeleteFramebuffersEXT(IntBuffer param1) {
checkLimit(param1); checkLimit(param1);
GLES20.glDeleteFramebuffers(param1.limit(), param1); GLES20.glDeleteFramebuffers(param1.limit(), param1);
} }
@Override
public void glDeleteRenderbuffersEXT(IntBuffer param1) { public void glDeleteRenderbuffersEXT(IntBuffer param1) {
checkLimit(param1); checkLimit(param1);
GLES20.glDeleteRenderbuffers(param1.limit(), param1); GLES20.glDeleteRenderbuffers(param1.limit(), param1);
} }
@Override
public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) { public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
GLES20.glFramebufferRenderbuffer(param1, param2, param3, param4); GLES20.glFramebufferRenderbuffer(param1, param2, param3, param4);
} }
@Override
public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) { public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
GLES20.glFramebufferTexture2D(param1, param2, param3, param4, param5); GLES20.glFramebufferTexture2D(param1, param2, param3, param4, param5);
} }
@Override
public void glGenFramebuffersEXT(IntBuffer param1) { public void glGenFramebuffersEXT(IntBuffer param1) {
checkLimit(param1); checkLimit(param1);
GLES20.glGenFramebuffers(param1.limit(), param1); GLES20.glGenFramebuffers(param1.limit(), param1);
} }
@Override
public void glGenRenderbuffersEXT(IntBuffer param1) { public void glGenRenderbuffersEXT(IntBuffer param1) {
checkLimit(param1); checkLimit(param1);
GLES20.glGenRenderbuffers(param1.limit(), param1); GLES20.glGenRenderbuffers(param1.limit(), param1);
} }
@Override
public void glGenerateMipmapEXT(int param1) { public void glGenerateMipmapEXT(int param1) {
GLES20.glGenerateMipmap(param1); GLES20.glGenerateMipmap(param1);
} }
@Override
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) { public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
GLES20.glRenderbufferStorage(param1, param2, param3, param4); GLES20.glRenderbufferStorage(param1, param2, param3, param4);
} }
@ -564,6 +670,58 @@ public class AndroidGL implements GL, GLExt, GLFbo {
@Override @Override
public void glFramebufferTextureLayerEXT(int target, int attachment, int texture, int level, int layer) { public void glFramebufferTextureLayerEXT(int target, int attachment, int texture, int level, int layer) {
throw new UnsupportedOperationException("OpenGL ES 2 does not support texture arrays"); GLES30.glFramebufferTextureLayer(target, attachment, texture, level, layer);
} }
@Override
public void glAlphaFunc(int func, float ref) {
}
@Override
public void glPointSize(float size) {
}
@Override
public void glPolygonMode(int face, int mode) {
}
// Wrapper to DrawBuffers as there's no DrawBuffer method in GLES
@Override
public void glDrawBuffer(int mode) {
tmpBuff.clear();
tmpBuff.put(0, mode);
tmpBuff.rewind();
glDrawBuffers(tmpBuff);
}
@Override
public void glReadBuffer(int mode) {
GLES30.glReadBuffer(mode);
}
@Override
public void glCompressedTexImage3D(int target, int level, int internalFormat, int width, int height, int depth,
int border, ByteBuffer data) {
GLES30.glCompressedTexImage3D(target, level, internalFormat, width, height, depth, border, getLimitBytes(data), data);
}
@Override
public void glCompressedTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width,
int height, int depth, int format, ByteBuffer data) {
GLES30.glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, getLimitBytes(data), data);
}
@Override
public void glTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border,
int format, int type, ByteBuffer data) {
GLES30.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, data);
}
@Override
public void glTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height,
int depth, int format, int type, ByteBuffer data) {
GLES30.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data);
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2015 jMonkeyEngine * Copyright (c) 2009-2019 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -38,7 +38,7 @@ import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGL11;
/** /**
* Utility class used by the {@link OGLESShaderRenderer renderer} and sister * Utility class used by the OGLESShaderRenderer and sister
* classes. * classes.
* *
* @author Kirill Vainer * @author Kirill Vainer

@ -19,6 +19,7 @@ public class AndroidConfigChooser implements EGLConfigChooser {
private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName()); private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName());
protected AppSettings settings; protected AppSettings settings;
private final static int EGL_OPENGL_ES2_BIT = 4; private final static int EGL_OPENGL_ES2_BIT = 4;
private final static int EGL_OPENGL_ES3_BIT = 0x40;
public AndroidConfigChooser(AppSettings settings) { public AndroidConfigChooser(AppSettings settings) {
this.settings = settings; this.settings = settings;
@ -140,13 +141,30 @@ public class AndroidConfigChooser implements EGLConfigChooser {
int[] num_config = new int[1]; int[] num_config = new int[1];
int[] configSpec = new int[]{ int[] configSpec = new int[]{
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL10.EGL_NONE}; EGL10.EGL_NONE};
boolean gles3=true;
// Try openGL ES 3
try {
if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
RendererUtil.checkEGLError(egl);
gles3=false;
}
} catch (com.jme3.renderer.RendererException re) {
// it's just the device not supporting GLES3. Fallback to GLES2
gles3=false;
}
if(!gles3)
{
// Get back to openGL ES 2
configSpec[1]=EGL_OPENGL_ES2_BIT;
if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) { if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
RendererUtil.checkEGLError(egl); RendererUtil.checkEGLError(egl);
throw new AssertionError(); throw new AssertionError();
} }
}
int numConfigs = num_config[0]; int numConfigs = num_config[0];
EGLConfig[] configs = new EGLConfig[numConfigs]; EGLConfig[] configs = new EGLConfig[numConfigs];

@ -204,6 +204,7 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
public void showSoftKeyboard(final boolean show) { public void showSoftKeyboard(final boolean show) {
view.getHandler().post(new Runnable() { view.getHandler().post(new Runnable() {
@Override
public void run() { public void run() {
InputMethodManager manager = InputMethodManager manager =
(InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2018 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -52,13 +52,10 @@ import com.jme3.input.controls.SoftTextDialogInputListener;
import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput; import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.renderer.android.AndroidGL; import com.jme3.renderer.android.AndroidGL;
import com.jme3.renderer.opengl.GL; import com.jme3.renderer.opengl.*;
import com.jme3.renderer.opengl.GLDebugES;
import com.jme3.renderer.opengl.GLExt;
import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.renderer.opengl.GLTracer;
import com.jme3.system.*; import com.jme3.system.*;
import com.jme3.util.AndroidBufferAllocator;
import com.jme3.util.BufferAllocatorFactory;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -81,6 +78,14 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
protected long minFrameDuration = 0; // No FPS cap protected long minFrameDuration = 0; // No FPS cap
protected long lastUpdateTime = 0; protected long lastUpdateTime = 0;
static {
final String implementation = BufferAllocatorFactory.PROPERTY_BUFFER_ALLOCATOR_IMPLEMENTATION;
if (System.getProperty(implementation) == null) {
System.setProperty(implementation, AndroidBufferAllocator.class.getName());
}
}
public OGLESContext() { public OGLESContext() {
} }
@ -99,13 +104,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
* @return GLSurfaceView The newly created view * @return GLSurfaceView The newly created view
*/ */
public GLSurfaceView createView(Context context) { public GLSurfaceView createView(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
// NOTE: We assume all ICS devices have OpenGL ES 2.0. // NOTE: We assume all ICS devices have OpenGL ES 2.0.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// below 4.0, check OpenGL ES 2.0 support. // below 4.0, check OpenGL ES 2.0 support.
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
if (info.reqGlEsVersion < 0x20000) { if (info.reqGlEsVersion < 0x20000) {
throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device"); throw new UnsupportedOperationException("OpenGL ES 2.0 or better is not supported on this device");
} }
} else if (Build.VERSION.SDK_INT < 9){ } else if (Build.VERSION.SDK_INT < 9){
throw new UnsupportedOperationException("jME3 requires Android 2.3 or later"); throw new UnsupportedOperationException("jME3 requires Android 2.3 or later");
@ -126,7 +131,8 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
// setEGLContextClientVersion must be set before calling setRenderer // setEGLContextClientVersion must be set before calling setRenderer
// this means it cannot be set in AndroidConfigChooser (too late) // this means it cannot be set in AndroidConfigChooser (too late)
view.setEGLContextClientVersion(2); // use proper openGL ES version
view.setEGLContextClientVersion(info.reqGlEsVersion>>16);
view.setFocusableInTouchMode(true); view.setFocusableInTouchMode(true);
view.setFocusable(true); view.setFocusable(true);
@ -190,20 +196,21 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
// Setup unhandled Exception Handler // Setup unhandled Exception Handler
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable thrown) { public void uncaughtException(Thread thread, Throwable thrown) {
listener.handleError("Exception thrown in " + thread.toString(), thrown); listener.handleError("Exception thrown in " + thread.toString(), thrown);
} }
}); });
timer = new NanoTimer(); timer = new NanoTimer();
Object gl = new AndroidGL(); GL gl = new AndroidGL();
if (settings.getBoolean("GraphicsDebug")) { if (settings.getBoolean("GraphicsDebug")) {
gl = new GLDebugES((GL) gl, (GLExt) gl, (GLFbo) gl); gl = (GL) GLDebug.createProxy(gl, gl, GL.class, GL2.class, GLES_30.class, GLFbo.class, GLExt.class);
} }
if (settings.getBoolean("GraphicsTrace")) { if (settings.getBoolean("GraphicsTrace")) {
gl = GLTracer.createGlesTracer(gl, GL.class, GLFbo.class, GLExt.class); gl = (GL)GLTracer.createGlesTracer(gl, GL.class, GLES_30.class, GLFbo.class, GLExt.class);
} }
renderer = new GLRenderer((GL)gl, (GLExt)gl, (GLFbo)gl); renderer = new GLRenderer(gl, (GLExt)gl, (GLFbo)gl);
renderer.initialize(); renderer.initialize();
JmeSystem.setSoftTextDialogInput(this); JmeSystem.setSoftTextDialogInput(this);
@ -242,7 +249,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
} }
if (settings.getFrameRate() > 0) { if (settings.getFrameRate() > 0) {
minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms minFrameDuration = (long)(1000d / settings.getFrameRate()); // ms
logger.log(Level.FINE, "Setting min tpf: {0}ms", minFrameDuration); logger.log(Level.FINE, "Setting min tpf: {0}ms", minFrameDuration);
} else { } else {
minFrameDuration = 0; minFrameDuration = 0;
@ -402,6 +409,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
} }
} }
@Override
public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) { public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) {
logger.log(Level.FINE, "requestDialog: title: {0}, initialValue: {1}", logger.log(Level.FINE, "requestDialog: title: {0}, initialValue: {1}",
new Object[]{title, initialValue}); new Object[]{title, initialValue});
@ -445,6 +453,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK", AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK",
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) { public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked OK, send COMPLETE action /* User clicked OK, send COMPLETE action
* and text */ * and text */
@ -452,6 +461,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
} }
}).setNegativeButton("Cancel", }).setNegativeButton("Cancel",
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) { public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked CANCEL, send CANCEL action /* User clicked CANCEL, send CANCEL action
* and text */ * and text */

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2014 jMonkeyEngine * Copyright (c) 2009-2020 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -67,6 +67,7 @@ public class AndroidBufferImageLoader implements AssetLoader {
} }
} }
@Override
public Object load(AssetInfo assetInfo) throws IOException { public Object load(AssetInfo assetInfo) throws IOException {
Bitmap bitmap = null; Bitmap bitmap = null;
Image.Format format; Image.Format format;

@ -25,12 +25,13 @@ public class AndroidNativeImageLoader implements AssetLoader {
private static native Image load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException; private static native Image load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException;
@Override
public Image load(AssetInfo info) throws IOException { public Image load(AssetInfo info) throws IOException {
boolean flip = ((TextureKey) info.getKey()).isFlipY(); boolean flip = ((TextureKey) info.getKey()).isFlipY();
InputStream in = null; InputStream in = null;
try { try {
in = info.openStream(); in = info.openStream();
return load(info.openStream(), flip, tmpArray); return load(in, flip, tmpArray);
} finally { } finally {
if (in != null){ if (in != null){
in.close(); in.close();

@ -0,0 +1,114 @@
/*
* Copyright (c) 2009-2019 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.util;
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
/**
* @author Jesus Oliver
*/
public class AndroidBufferAllocator implements BufferAllocator {
// We make use of the ReflectionAllocator to remove the inner buffer
private static final ReflectionAllocator reflectionAllocator = new ReflectionAllocator();
private static final String[] wrapperClassNames = {
"java.nio.ByteBufferAsFloatBuffer",
"java.nio.ByteBufferAsIntBuffer",
"java.nio.ByteBufferAsDoubleBuffer",
"java.nio.ByteBufferAsShortBuffer",
"java.nio.ByteBufferAsLongBuffer",
"java.nio.ByteBufferAsCharBuffer",
};
private static final String[] possibleBufferFieldNames = {"bb", "byteBuffer"};
// Keep track of ByteBuffer field by the wrapper class
private static final Map<Class, Field> fieldIndex = new HashMap<>();
static {
for (String className : wrapperClassNames) {
try {
Class clazz = Class.forName(className);
// loop for all possible field names in android
for (String fieldName : possibleBufferFieldNames) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
fieldIndex.put(clazz, field);
break;
} catch (NoSuchFieldException e) {
}
}
} catch (ClassNotFoundException ex) {
}
}
}
@Override
/**
* This function search the inner direct buffer of the android specific wrapped buffer classes
* and destroys it using the reflection allocator method.
*
* @param toBeDestroyed The direct buffer that will be "cleaned".
*
*/
public void destroyDirectBuffer(Buffer toBeDestroyed) {
// If it is a wrapped buffer, get it's inner direct buffer field and destroy it
Field field = fieldIndex.get(toBeDestroyed.getClass());
if (field != null) {
try {
ByteBuffer innerBuffer = (ByteBuffer) field.get(toBeDestroyed);
if (innerBuffer != null) {
// Destroy it using the reflection method
reflectionAllocator.destroyDirectBuffer(innerBuffer);
}
} catch (IllegalAccessException ex) {
}
} else {
// It is not a wrapped buffer, use default reflection allocator to remove it instead.
reflectionAllocator.destroyDirectBuffer(toBeDestroyed);
}
}
@Override
public ByteBuffer allocate(int size) {
return ByteBuffer.allocateDirect(size);
}
}

@ -49,6 +49,7 @@ public class RingBuffer<T> implements Iterable<T> {
return item; return item;
} }
@Override
public Iterator<T> iterator() { public Iterator<T> iterator() {
return new RingBufferIterator(); return new RingBufferIterator();
} }
@ -58,14 +59,17 @@ public class RingBuffer<T> implements Iterable<T> {
private int i = 0; private int i = 0;
@Override
public boolean hasNext() { public boolean hasNext() {
return i < count; return i < count;
} }
@Override
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public T next() { public T next() {
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();

@ -1,12 +0,0 @@
if (!hasProperty('mainClass')) {
ext.mainClass = ''
}
dependencies {
compile project(':jme3-core')
compile project(':jme3-desktop')
compile project(':jme3-effects')
compile ('org.ejml:core:0.27')
compile ('org.ejml:dense64:0.27')
compile ('org.ejml:simple:0.27')
}

@ -1,733 +0,0 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.asset;
import java.io.IOException;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
/**
* Blender key. Contains path of the blender file and its loading properties.
* @author Marcin Roguski (Kaelthas)
*/
public class BlenderKey extends ModelKey {
protected static final int DEFAULT_FPS = 25;
/**
* FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
* between the frames.
*/
protected int fps = DEFAULT_FPS;
/**
* This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
*/
protected int featuresToLoad = FeaturesToLoad.ALL;
/** The variable that tells if content of the file (along with data unlinked to any feature on the scene) should be stored as 'user data' in the result spatial. */
protected boolean loadUnlinkedAssets;
/** The root path for all the assets. */
protected String assetRootPath;
/** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
protected boolean fixUpAxis = true;
/** Generated textures resolution (PPU - Pixels Per Unit). */
protected int generatedTexturePPU = 128;
/**
* The name of world settings that the importer will use. If not set or specified name does not occur in the file
* then the first world settings in the file will be used.
*/
protected String usedWorld;
/**
* User's default material that is set for objects that have no material definition in blender. The default value is
* null. If the value is null the importer will use its own default material (gray color - like in blender).
*/
protected Material defaultMaterial;
/** Face cull mode. By default it is disabled. */
protected FaceCullMode faceCullMode = FaceCullMode.Back;
/**
* Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
* If set to -1 then the current layer will be loaded.
*/
protected int layersToLoad = -1;
/** A variable that toggles the object custom properties loading. */
protected boolean loadObjectProperties = true;
/**
* Maximum texture size. Might be dependant on the graphic card.
* This value is taken from <b>org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE</b>.
*/
protected int maxTextureSize = 8192;
/** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
protected boolean loadGeneratedTextures;
/** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
protected MipmapGenerationMethod mipmapGenerationMethod = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
/**
* If the sky has only generated textures applied then they will have the following size (both width and height). If 2d textures are used then the generated
* textures will get their proper size.
*/
protected int skyGeneratedTextureSize = 1000;
/** The radius of a shape that will be used while creating the generated texture for the sky. The higher it is the larger part of the texture will be seen. */
protected float skyGeneratedTextureRadius = 1;
/** The shape against which the generated texture for the sky will be created. */
protected SkyGeneratedTextureShape skyGeneratedTextureShape = SkyGeneratedTextureShape.SPHERE;
/**
* This field tells if the importer should optimise the use of textures or not. If set to true, then textures of the same mapping type will be merged together
* and textures that in the final result will never be visible - will be discarded.
*/
protected boolean optimiseTextures;
/** The method of matching animations to skeletons. The default value is: AT_LEAST_ONE_NAME_MATCH. */
protected AnimationMatchMethod animationMatchMethod = AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH;
/** The size of points that are loaded and do not belong to any edge of the mesh. */
protected float pointsSize = 1;
/** The width of edges that are loaded from the mesh and do not belong to any face. */
protected float linesWidth = 1;
/**
* Constructor used by serialization mechanisms.
*/
public BlenderKey() {
}
/**
* Constructor. Creates a key for the given file name.
* @param name
* the name (path) of a file
*/
public BlenderKey(String name) {
super(name);
}
/**
* This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
* @return the frames per second amount
*/
public int getFps() {
return fps;
}
/**
* This method sets frames per second amount.
* @param fps
* the frames per second amount
*/
public void setFps(int fps) {
this.fps = fps;
}
/**
* This method returns the face cull mode.
* @return the face cull mode
*/
public FaceCullMode getFaceCullMode() {
return faceCullMode;
}
/**
* This method sets the face cull mode.
* @param faceCullMode
* the face cull mode
*/
public void setFaceCullMode(FaceCullMode faceCullMode) {
this.faceCullMode = faceCullMode;
}
/**
* This method sets layers to be loaded.
* @param layersToLoad
* layers to be loaded
*/
public void setLayersToLoad(int layersToLoad) {
this.layersToLoad = layersToLoad;
}
/**
* This method returns layers to be loaded.
* @return layers to be loaded
*/
public int getLayersToLoad() {
return layersToLoad;
}
/**
* This method sets the properies loading policy.
* By default the value is true.
* @param loadObjectProperties
* true to load properties and false to suspend their loading
*/
public void setLoadObjectProperties(boolean loadObjectProperties) {
this.loadObjectProperties = loadObjectProperties;
}
/**
* @return the current properties loading properties
*/
public boolean isLoadObjectProperties() {
return loadObjectProperties;
}
/**
* The default value for this parameter is the same as defined by: org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE.
* If by any means this is too large for user's hardware configuration use the 'setMaxTextureSize' method to change that.
* @return maximum texture size (width/height)
*/
public int getMaxTextureSize() {
return maxTextureSize;
}
/**
* This method sets the maximum texture size.
* @param maxTextureSize
* the maximum texture size
*/
public void setMaxTextureSize(int maxTextureSize) {
this.maxTextureSize = maxTextureSize;
}
/**
* This method sets the flag that toggles the generated textures loading.
* @param loadGeneratedTextures
* <b>true</b> if generated textures should be loaded and <b>false</b> otherwise
*/
public void setLoadGeneratedTextures(boolean loadGeneratedTextures) {
this.loadGeneratedTextures = loadGeneratedTextures;
}
/**
* @return tells if the generated textures should be loaded (<b>false</b> is the default value)
*/
public boolean isLoadGeneratedTextures() {
return loadGeneratedTextures;
}
/**
* Not used any more.
* This method sets the asset root path.
* @param assetRootPath
* the assets root path
*/
@Deprecated
public void setAssetRootPath(String assetRootPath) {
this.assetRootPath = assetRootPath;
}
/**
* Not used any more.
* This method returns the asset root path.
* @return the asset root path
*/
@Deprecated
public String getAssetRootPath() {
return assetRootPath;
}
/**
* This method adds features to be loaded.
* @param featuresToLoad
* bitwise flag of FeaturesToLoad interface values
*/
@Deprecated
public void includeInLoading(int featuresToLoad) {
this.featuresToLoad |= featuresToLoad;
}
/**
* This method removes features from being loaded.
* @param featuresNotToLoad
* bitwise flag of FeaturesToLoad interface values
*/
@Deprecated
public void excludeFromLoading(int featuresNotToLoad) {
featuresToLoad &= ~featuresNotToLoad;
}
@Deprecated
public boolean shouldLoad(int featureToLoad) {
return (featuresToLoad & featureToLoad) != 0;
}
/**
* This method returns bitwise value of FeaturesToLoad interface value. It describes features that will be loaded by
* the blender file loader.
* @return features that will be loaded by the blender file loader
*/
@Deprecated
public int getFeaturesToLoad() {
return featuresToLoad;
}
/**
* This method determines if unlinked assets should be loaded.
* If not then only objects on selected layers will be loaded and their assets if required.
* If yes then all assets will be loaded even if they are on inactive layers or are not linked
* to anything.
* @return <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
*/
public boolean isLoadUnlinkedAssets() {
return loadUnlinkedAssets;
}
/**
* This method sets if unlinked assets should be loaded.
* If not then only objects on selected layers will be loaded and their assets if required.
* If yes then all assets will be loaded even if they are on inactive layers or are not linked
* to anything.
* @param loadUnlinkedAssets
* <b>true</b> if unlinked assets should be loaded and <b>false</b> otherwise
*/
public void setLoadUnlinkedAssets(boolean loadUnlinkedAssets) {
this.loadUnlinkedAssets = loadUnlinkedAssets;
}
/**
* This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y
* is up axis.
* @param fixUpAxis
* the up axis state variable
*/
public void setFixUpAxis(boolean fixUpAxis) {
this.fixUpAxis = fixUpAxis;
}
/**
* This method returns the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By
* default Y is up axis.
* @return the up axis state variable
*/
public boolean isFixUpAxis() {
return fixUpAxis;
}
/**
* This method sets the generated textures resolution.
* @param generatedTexturePPU
* the generated textures resolution
*/
public void setGeneratedTexturePPU(int generatedTexturePPU) {
this.generatedTexturePPU = generatedTexturePPU;
}
/**
* @return the generated textures resolution
*/
public int getGeneratedTexturePPU() {
return generatedTexturePPU;
}
/**
* @return mipmaps generation method
*/
public MipmapGenerationMethod getMipmapGenerationMethod() {
return mipmapGenerationMethod;
}
/**
* @param mipmapGenerationMethod
* mipmaps generation method
*/
public void setMipmapGenerationMethod(MipmapGenerationMethod mipmapGenerationMethod) {
this.mipmapGenerationMethod = mipmapGenerationMethod;
}
/**
* @return the size of the generated textures for the sky (used if no flat textures are applied)
*/
public int getSkyGeneratedTextureSize() {
return skyGeneratedTextureSize;
}
/**
* @param skyGeneratedTextureSize
* the size of the generated textures for the sky (used if no flat textures are applied)
*/
public void setSkyGeneratedTextureSize(int skyGeneratedTextureSize) {
if (skyGeneratedTextureSize <= 0) {
throw new IllegalArgumentException("The texture size must be a positive value (the value given as a parameter: " + skyGeneratedTextureSize + ")!");
}
this.skyGeneratedTextureSize = skyGeneratedTextureSize;
}
/**
* @return the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
*/
public float getSkyGeneratedTextureRadius() {
return skyGeneratedTextureRadius;
}
/**
* @param skyGeneratedTextureRadius
* the radius of a shape that will be used while creating the generated texture for the sky, the higher it is the larger part of the texture will be seen
*/
public void setSkyGeneratedTextureRadius(float skyGeneratedTextureRadius) {
this.skyGeneratedTextureRadius = skyGeneratedTextureRadius;
}
/**
* @return the shape against which the generated texture for the sky will be created (by default it is a sphere).
*/
public SkyGeneratedTextureShape getSkyGeneratedTextureShape() {
return skyGeneratedTextureShape;
}
/**
* @param skyGeneratedTextureShape
* the shape against which the generated texture for the sky will be created
*/
public void setSkyGeneratedTextureShape(SkyGeneratedTextureShape skyGeneratedTextureShape) {
if (skyGeneratedTextureShape == null) {
throw new IllegalArgumentException("The sky generated shape type cannot be null!");
}
this.skyGeneratedTextureShape = skyGeneratedTextureShape;
}
/**
* If set to true, then textures of the same mapping type will be merged together
* and textures that in the final result will never be visible - will be discarded.
* @param optimiseTextures
* the variable that tells if the textures should be optimised or not
*/
public void setOptimiseTextures(boolean optimiseTextures) {
this.optimiseTextures = optimiseTextures;
}
/**
* @return the variable that tells if the textures should be optimised or not (by default the optimisation is disabled)
*/
public boolean isOptimiseTextures() {
return optimiseTextures;
}
/**
* Sets the way the animations will be matched with skeletons.
*
* @param animationMatchMethod
* the way the animations will be matched with skeletons
*/
public void setAnimationMatchMethod(AnimationMatchMethod animationMatchMethod) {
this.animationMatchMethod = animationMatchMethod;
}
/**
* @return the way the animations will be matched with skeletons
*/
public AnimationMatchMethod getAnimationMatchMethod() {
return animationMatchMethod;
}
/**
* @return the size of points that are loaded and do not belong to any edge of the mesh
*/
public float getPointsSize() {
return pointsSize;
}
/**
* Sets the size of points that are loaded and do not belong to any edge of the mesh.
* @param pointsSize
* The size of points that are loaded and do not belong to any edge of the mesh
*/
public void setPointsSize(float pointsSize) {
this.pointsSize = pointsSize;
}
/**
* @return the width of edges that are loaded from the mesh and do not belong to any face
*/
public float getLinesWidth() {
return linesWidth;
}
/**
* Sets the width of edges that are loaded from the mesh and do not belong to any face.
* @param linesWidth
* the width of edges that are loaded from the mesh and do not belong to any face
*/
public void setLinesWidth(float linesWidth) {
this.linesWidth = linesWidth;
}
/**
* This method sets the name of the WORLD data block that should be used during file loading. By default the name is
* not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
* during loading (assuming any exists in the file).
* @param usedWorld
* the name of the WORLD block used during loading
*/
public void setUsedWorld(String usedWorld) {
this.usedWorld = usedWorld;
}
/**
* This method returns the name of the WORLD data block that should be used during file loading.
* @return the name of the WORLD block used during loading
*/
public String getUsedWorld() {
return usedWorld;
}
/**
* This method sets the default material for objects.
* @param defaultMaterial
* the default material
*/
public void setDefaultMaterial(Material defaultMaterial) {
this.defaultMaterial = defaultMaterial;
}
/**
* This method returns the default material.
* @return the default material
*/
public Material getDefaultMaterial() {
return defaultMaterial;
}
@Override
public void write(JmeExporter e) throws IOException {
super.write(e);
OutputCapsule oc = e.getCapsule(this);
oc.write(fps, "fps", DEFAULT_FPS);
oc.write(featuresToLoad, "features-to-load", FeaturesToLoad.ALL);
oc.write(loadUnlinkedAssets, "load-unlinked-assets", false);
oc.write(assetRootPath, "asset-root-path", null);
oc.write(fixUpAxis, "fix-up-axis", true);
oc.write(generatedTexturePPU, "generated-texture-ppu", 128);
oc.write(usedWorld, "used-world", null);
oc.write(defaultMaterial, "default-material", null);
oc.write(faceCullMode, "face-cull-mode", FaceCullMode.Off);
oc.write(layersToLoad, "layers-to-load", -1);
oc.write(mipmapGenerationMethod, "mipmap-generation-method", MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
oc.write(skyGeneratedTextureSize, "sky-generated-texture-size", 1000);
oc.write(skyGeneratedTextureRadius, "sky-generated-texture-radius", 1f);
oc.write(skyGeneratedTextureShape, "sky-generated-texture-shape", SkyGeneratedTextureShape.SPHERE);
oc.write(optimiseTextures, "optimise-textures", false);
oc.write(animationMatchMethod, "animation-match-method", AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH);
oc.write(pointsSize, "points-size", 1);
oc.write(linesWidth, "lines-width", 1);
}
@Override
public void read(JmeImporter e) throws IOException {
super.read(e);
InputCapsule ic = e.getCapsule(this);
fps = ic.readInt("fps", DEFAULT_FPS);
featuresToLoad = ic.readInt("features-to-load", FeaturesToLoad.ALL);
loadUnlinkedAssets = ic.readBoolean("load-unlinked-assets", false);
assetRootPath = ic.readString("asset-root-path", null);
fixUpAxis = ic.readBoolean("fix-up-axis", true);
generatedTexturePPU = ic.readInt("generated-texture-ppu", 128);
usedWorld = ic.readString("used-world", null);
defaultMaterial = (Material) ic.readSavable("default-material", null);
faceCullMode = ic.readEnum("face-cull-mode", FaceCullMode.class, FaceCullMode.Off);
layersToLoad = ic.readInt("layers-to=load", -1);
mipmapGenerationMethod = ic.readEnum("mipmap-generation-method", MipmapGenerationMethod.class, MipmapGenerationMethod.GENERATE_WHEN_NEEDED);
skyGeneratedTextureSize = ic.readInt("sky-generated-texture-size", 1000);
skyGeneratedTextureRadius = ic.readFloat("sky-generated-texture-radius", 1f);
skyGeneratedTextureShape = ic.readEnum("sky-generated-texture-shape", SkyGeneratedTextureShape.class, SkyGeneratedTextureShape.SPHERE);
optimiseTextures = ic.readBoolean("optimise-textures", false);
animationMatchMethod = ic.readEnum("animation-match-method", AnimationMatchMethod.class, AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH);
pointsSize = ic.readFloat("points-size", 1);
linesWidth = ic.readFloat("lines-width", 1);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + (animationMatchMethod == null ? 0 : animationMatchMethod.hashCode());
result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
result = prime * result + featuresToLoad;
result = prime * result + (fixUpAxis ? 1231 : 1237);
result = prime * result + fps;
result = prime * result + generatedTexturePPU;
result = prime * result + layersToLoad;
result = prime * result + (loadGeneratedTextures ? 1231 : 1237);
result = prime * result + (loadObjectProperties ? 1231 : 1237);
result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
result = prime * result + maxTextureSize;
result = prime * result + (mipmapGenerationMethod == null ? 0 : mipmapGenerationMethod.hashCode());
result = prime * result + (optimiseTextures ? 1231 : 1237);
result = prime * result + Float.floatToIntBits(skyGeneratedTextureRadius);
result = prime * result + (skyGeneratedTextureShape == null ? 0 : skyGeneratedTextureShape.hashCode());
result = prime * result + skyGeneratedTextureSize;
result = prime * result + (usedWorld == null ? 0 : usedWorld.hashCode());
result = prime * result + (int) pointsSize;
result = prime * result + (int) linesWidth;
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BlenderKey) {
return false;
}
BlenderKey other = (BlenderKey) obj;
if (animationMatchMethod != other.animationMatchMethod) {
return false;
}
if (assetRootPath == null) {
if (other.assetRootPath != null) {
return false;
}
} else if (!assetRootPath.equals(other.assetRootPath)) {
return false;
}
if (defaultMaterial == null) {
if (other.defaultMaterial != null) {
return false;
}
} else if (!defaultMaterial.equals(other.defaultMaterial)) {
return false;
}
if (faceCullMode != other.faceCullMode) {
return false;
}
if (featuresToLoad != other.featuresToLoad) {
return false;
}
if (fixUpAxis != other.fixUpAxis) {
return false;
}
if (fps != other.fps) {
return false;
}
if (generatedTexturePPU != other.generatedTexturePPU) {
return false;
}
if (layersToLoad != other.layersToLoad) {
return false;
}
if (loadGeneratedTextures != other.loadGeneratedTextures) {
return false;
}
if (loadObjectProperties != other.loadObjectProperties) {
return false;
}
if (loadUnlinkedAssets != other.loadUnlinkedAssets) {
return false;
}
if (maxTextureSize != other.maxTextureSize) {
return false;
}
if (mipmapGenerationMethod != other.mipmapGenerationMethod) {
return false;
}
if (optimiseTextures != other.optimiseTextures) {
return false;
}
if (Float.floatToIntBits(skyGeneratedTextureRadius) != Float.floatToIntBits(other.skyGeneratedTextureRadius)) {
return false;
}
if (skyGeneratedTextureShape != other.skyGeneratedTextureShape) {
return false;
}
if (skyGeneratedTextureSize != other.skyGeneratedTextureSize) {
return false;
}
if (usedWorld == null) {
if (other.usedWorld != null) {
return false;
}
} else if (!usedWorld.equals(other.usedWorld)) {
return false;
}
if (pointsSize != other.pointsSize) {
return false;
}
if (linesWidth != other.linesWidth) {
return false;
}
return true;
}
/**
* This enum tells the importer if the mipmaps for textures will be generated by jme. <li>NEVER_GENERATE and ALWAYS_GENERATE are quite understandable <li>GENERATE_WHEN_NEEDED is an option that checks if the texture had 'Generate mipmaps' option set in blender, mipmaps are generated only when the option is set
* @author Marcin Roguski (Kaelthas)
*/
public static enum MipmapGenerationMethod {
NEVER_GENERATE, ALWAYS_GENERATE, GENERATE_WHEN_NEEDED;
}
/**
* This interface describes the features of the scene that are to be loaded.
* @deprecated this interface is deprecated and is not used anymore; to ensure the loading models consistency
* everything must be loaded because in blender one feature might depend on another
* @author Marcin Roguski (Kaelthas)
*/
@Deprecated
public static interface FeaturesToLoad {
int SCENES = 0x0000FFFF;
int OBJECTS = 0x0000000B;
int ANIMATIONS = 0x00000004;
int MATERIALS = 0x00000003;
int TEXTURES = 0x00000001;
int CAMERAS = 0x00000020;
int LIGHTS = 0x00000010;
int WORLD = 0x00000040;
int ALL = 0xFFFFFFFF;
}
/**
* The shape againts which the sky generated texture will be created.
*
* @author Marcin Roguski (Kaelthas)
*/
public static enum SkyGeneratedTextureShape {
CUBE, SPHERE;
}
/**
* This enum describes which animations should be attached to which armature.
* Blender does not store the mapping between action and armature. That is why the importer
* will try to match those by comparing bone name of the armature with the channel names
* int the actions.
*
* @author Marcin Roguski (Kaelthas)
*/
public static enum AnimationMatchMethod {
/**
* Animation is matched with skeleton when at leas one bone name matches the name of the action channel.
* All the bones that do not have their corresponding channel in the animation will not get the proper tracks for
* this particulat animation.
* Also the channel will not be used for the animation if it does not find the proper bone name.
*/
AT_LEAST_ONE_NAME_MATCH,
/**
* Animation is matched when all action names are covered by the target names (bone names or the name of the
* animated spatial.
*/
ALL_NAMES_MATCH;
}
}

@ -1,193 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene.plugins.blender;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.Properties;
/**
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
* hold the state of the calculations.
* @author Marcin Roguski
*/
public abstract class AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName());
/** The blender context. */
protected BlenderContext blenderContext;
/** The version of the blend file. */
protected final int blenderVersion;
/** This variable indicates if the Y asxis is the UP axis or not. */
protected boolean fixUpAxis;
/** Quaternion used to rotate data when Y is up axis. */
protected Quaternion upAxisRotationQuaternion;
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
* versions.
* @param blenderVersion
* the version read from the blend file
* @param blenderContext
* the blender context
*/
public AbstractBlenderHelper(String blenderVersion, BlenderContext blenderContext) {
this.blenderVersion = Integer.parseInt(blenderVersion);
this.blenderContext = blenderContext;
fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
if (fixUpAxis) {
upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);
}
}
/**
* This method loads the properties if they are available and defined for the structure.
* @param structure
* the structure we read the properties from
* @param blenderContext
* the blender context
* @return loaded properties or null if they are not available
* @throws BlenderFileException
* an exception is thrown when the blend file is somehow corrupted
*/
protected Properties loadProperties(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
Properties properties = null;
Structure id = (Structure) structure.getFieldValue("ID");
if (id != null) {
Pointer pProperties = (Pointer) id.getFieldValue("properties");
if (pProperties.isNotNull()) {
Structure propertiesStructure = pProperties.fetchData().get(0);
properties = new Properties();
properties.load(propertiesStructure, blenderContext);
}
}
return properties;
}
/**
* The method applies properties to the given spatial. The Properties
* instance cannot be directly applied because the end-user might not have
* the blender plugin jar file and thus receive ClassNotFoundException. The
* values are set by name instead.
*
* @param spatial
* the spatial that is to have properties applied
* @param properties
* the properties to be applied
*/
public void applyProperties(Spatial spatial, Properties properties) {
List<String> propertyNames = properties.getSubPropertiesNames();
if (propertyNames != null && propertyNames.size() > 0) {
for (String propertyName : propertyNames) {
Object value = properties.findValue(propertyName);
if (value instanceof Savable || value instanceof Boolean || value instanceof String || value instanceof Float || value instanceof Integer || value instanceof Long) {
spatial.setUserData(propertyName, value);
} else if (value instanceof Double) {
spatial.setUserData(propertyName, ((Double) value).floatValue());
} else if (value instanceof int[]) {
spatial.setUserData(propertyName, Arrays.toString((int[]) value));
} else if (value instanceof float[]) {
spatial.setUserData(propertyName, Arrays.toString((float[]) value));
} else if (value instanceof double[]) {
spatial.setUserData(propertyName, Arrays.toString((double[]) value));
}
}
}
}
/**
* The method loads library of a given ID from linked blender file.
* @param id
* the ID of the linked feature (it contains its name and blender path)
* @return loaded feature or null if none was found
* @throws BlenderFileException
* and exception is throw when problems with reading a blend file occur
*/
protected Object loadLibrary(Structure id) throws BlenderFileException {
Pointer pLib = (Pointer) id.getFieldValue("lib");
if (pLib.isNotNull()) {
String fullName = id.getFieldValue("name").toString();// we need full name with the prefix
String nameOfFeatureToLoad = id.getName();
Structure library = pLib.fetchData().get(0);
String path = library.getFieldValue("filepath").toString();
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
Spatial loadedAsset = null;
BlenderKey blenderKey = new BlenderKey(path);
blenderKey.setLoadUnlinkedAssets(true);
try {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
} catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
}
if (loadedAsset != null) {
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
}
} else {
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);
}
}
Object result = blenderContext.getLinkedFeature(path, fullName);
if (result == null) {
LOGGER.log(Level.WARNING, "Could NOT find asset named {0} in the library of path: {1}.", new Object[] { nameOfFeatureToLoad, path });
} else {
blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.STRUCTURE, id);
blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
}
return result;
} else {
LOGGER.warning("Library link points to nothing!");
}
return null;
}
}

@ -1,770 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene.plugins.blender;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.light.Light;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.animations.BlenderAction;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.Constraint;
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.DnaBlockData;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.texture.Texture;
/**
* The class that stores temporary data and manages it during loading the belnd
* file. This class is intended to be used in a single loading thread. It holds
* the state of loading operations.
*
* @author Marcin Roguski (Kaelthas)
*/
public class BlenderContext {
/** The blender file version. */
private int blenderVersion;
/** The blender key. */
private BlenderKey blenderKey;
/** The header of the file block. */
private DnaBlockData dnaBlockData;
/** The scene structure. */
private Structure sceneStructure;
/** The input stream of the blend file. */
private BlenderInputStream inputStream;
/** The asset manager. */
private AssetManager assetManager;
/** The blocks read from the file. */
protected List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
/**
* A map containing the file block headers. The key is the old memory address.
*/
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
/** A map containing the file block headers. The key is the block code. */
private Map<BlockCode, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<BlockCode, List<FileBlockHeader>>();
/**
* This map stores the loaded features by their old memory address. The
* first object in the value table is the loaded structure and the second -
* the structure already converted into proper data.
*/
private Map<Long, Map<LoadedDataType, Object>> loadedFeatures = new HashMap<Long, Map<LoadedDataType, Object>>();
/** Features loaded from external blender files. The key is the file path and the value is a map between feature name and loaded feature. */
private Map<String, Map<String, Object>> linkedFeatures = new HashMap<String, Map<String, Object>>();
/** A stack that hold the parent structure of currently loaded feature. */
private Stack<Structure> parentStack = new Stack<Structure>();
/** A list of constraints for the specified object. */
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
/** Animations loaded for features. */
private Map<Long, List<Animation>> animations = new HashMap<Long, List<Animation>>();
/** Loaded skeletons. */
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
/** A map between skeleton and node it modifies. */
private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>();
/** A map of bone contexts. */
protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>();
/** A map og helpers that perform loading. */
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
/** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */
private Map<String, Map<Object, Object>> markers = new HashMap<String, Map<Object, Object>>();
/** A map of blender actions. The key is the action name and the value is the action itself. */
private Map<String, BlenderAction> actions = new HashMap<String, BlenderAction>();
/**
* This method sets the blender file version.
*
* @param blenderVersion
* the blender file version
*/
public void setBlenderVersion(String blenderVersion) {
this.blenderVersion = Integer.parseInt(blenderVersion);
}
/**
* @return the blender file version
*/
public int getBlenderVersion() {
return blenderVersion;
}
/**
* This method sets the blender key.
*
* @param blenderKey
* the blender key
*/
public void setBlenderKey(BlenderKey blenderKey) {
this.blenderKey = blenderKey;
}
/**
* This method returns the blender key.
*
* @return the blender key
*/
public BlenderKey getBlenderKey() {
return blenderKey;
}
/**
* This method sets the dna block data.
*
* @param dnaBlockData
* the dna block data
*/
public void setBlockData(DnaBlockData dnaBlockData) {
this.dnaBlockData = dnaBlockData;
}
/**
* This method returns the dna block data.
*
* @return the dna block data
*/
public DnaBlockData getDnaBlockData() {
return dnaBlockData;
}
/**
* This method sets the scene structure data.
*
* @param sceneStructure
* the scene structure data
*/
public void setSceneStructure(Structure sceneStructure) {
this.sceneStructure = sceneStructure;
}
/**
* This method returns the scene structure data.
*
* @return the scene structure data
*/
public Structure getSceneStructure() {
return sceneStructure;
}
/**
* This method returns the asset manager.
*
* @return the asset manager
*/
public AssetManager getAssetManager() {
return assetManager;
}
/**
* This method sets the asset manager.
*
* @param assetManager
* the asset manager
*/
public void setAssetManager(AssetManager assetManager) {
this.assetManager = assetManager;
}
/**
* This method returns the input stream of the blend file.
*
* @return the input stream of the blend file
*/
public BlenderInputStream getInputStream() {
return inputStream;
}
/**
* This method sets the input stream of the blend file.
*
* @param inputStream
* the input stream of the blend file
*/
public void setInputStream(BlenderInputStream inputStream) {
this.inputStream = inputStream;
}
/**
* This method adds a file block header to the map. Its old memory address
* is the key.
*
* @param oldMemoryAddress
* the address of the block header
* @param fileBlockHeader
* the block header to store
*/
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
blocks.add(fileBlockHeader);
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) {
headers = new ArrayList<FileBlockHeader>();
fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers);
}
headers.add(fileBlockHeader);
}
/**
* @return the block headers
*/
public List<FileBlockHeader> getBlocks() {
return blocks;
}
/**
* This method returns the block header of a given memory address. If the
* header is not present then null is returned.
*
* @param oldMemoryAddress
* the address of the block header
* @return loaded header or null if it was not yet loaded
*/
public FileBlockHeader getFileBlock(Long oldMemoryAddress) {
return fileBlockHeadersByOma.get(oldMemoryAddress);
}
/**
* This method returns a list of file blocks' headers of a specified code.
*
* @param code
* the code of file blocks
* @return a list of file blocks' headers of a specified code
*/
public List<FileBlockHeader> getFileBlocks(BlockCode code) {
return fileBlockHeadersByCode.get(code);
}
/**
* This method adds a helper instance to the helpers' map.
*
* @param <T>
* the type of the helper
* @param clazz
* helper's class definition
* @param helper
* the helper instance
*/
public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
helpers.put(clazz.getSimpleName(), helper);
}
@SuppressWarnings("unchecked")
public <T> T getHelper(Class<?> clazz) {
return (T) helpers.get(clazz.getSimpleName());
}
/**
* This method adds a loaded feature to the map. The key is its unique old
* memory address.
*
* @param oldMemoryAddress
* the address of the feature
* @param featureName
* the name of the feature
* @param structure
* the filled structure of the feature
* @param feature
* the feature we want to store
*/
public void addLoadedFeatures(Long oldMemoryAddress, LoadedDataType featureDataType, Object feature) {
if (oldMemoryAddress == null || featureDataType == null || feature == null) {
throw new IllegalArgumentException("One of the given arguments is null!");
}
Map<LoadedDataType, Object> map = loadedFeatures.get(oldMemoryAddress);
if (map == null) {
map = new HashMap<BlenderContext.LoadedDataType, Object>();
loadedFeatures.put(oldMemoryAddress, map);
}
map.put(featureDataType, feature);
}
/**
* This method returns the feature of a given memory address. If the feature
* is not yet loaded then null is returned.
*
* @param oldMemoryAddress
* the address of the feature
* @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled
* structure or already converted feature
* @return loaded feature or null if it was not yet loaded
*/
public Object getLoadedFeature(Long oldMemoryAddress, LoadedDataType loadedFeatureDataType) {
Map<LoadedDataType, Object> result = loadedFeatures.get(oldMemoryAddress);
if (result != null) {
return result.get(loadedFeatureDataType);
}
return null;
}
/**
* The method adds linked content to the blender context.
* @param blenderFilePath
* the path of linked blender file
* @param featureGroup
* the linked feature group (ie. scenes, materials, meshes, etc.)
* @param feature
* the linked feature
*/
@Deprecated
public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
// the method is deprecated and empty at the moment
}
/**
* The method returns linked feature of a given name from the specified blender path.
* @param blenderFilePath
* the blender file path
* @param featureName
* the feature name we want to get
* @return linked feature or null if none was found
*/
@SuppressWarnings("unchecked")
public Object getLinkedFeature(String blenderFilePath, String featureName) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
if(linkedFeatures != null) {
String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase();
featureName = featureName.substring(2);
if("SC".equals(namePrefix)) {
List<Node> scenes = (List<Node>) linkedFeatures.get("scenes");
if(scenes != null) {
for(Node scene : scenes) {
if(featureName.equals(scene.getName())) {
return scene;
}
}
}
} else if("OB".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("objects");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("ME".equals(namePrefix)) {
List<TemporalMesh> temporalMeshes = (List<TemporalMesh>) linkedFeatures.get("meshes");
if(temporalMeshes != null) {
for(TemporalMesh temporalMesh : temporalMeshes) {
if(featureName.equals(temporalMesh.getName())) {
return temporalMesh;
}
}
}
} else if("MA".equals(namePrefix)) {
List<MaterialContext> features = (List<MaterialContext>) linkedFeatures.get("materials");
if(features != null) {
for(MaterialContext feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("TX".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("textures");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("IM".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("images");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("AC".equals(namePrefix)) {
List<Animation> features = (List<Animation>) linkedFeatures.get("animations");
if(features != null) {
for(Animation feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("CA".equals(namePrefix)) {
List<Camera> features = (List<Camera>) linkedFeatures.get("cameras");
if(features != null) {
for(Camera feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("LA".equals(namePrefix)) {
List<Light> features = (List<Light>) linkedFeatures.get("lights");
if(features != null) {
for(Light feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("FI".equals(featureName)) {
List<Filter> features = (List<Filter>) linkedFeatures.get("lights");
if(features != null) {
for(Filter feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
}
}
return null;
}
/**
* @return all linked features for the current blend file
*/
public Map<String, Map<String, Object>> getLinkedFeatures() {
return linkedFeatures;
}
/**
* This method adds the structure to the parent stack.
*
* @param parent
* the structure to be added to the stack
*/
public void pushParent(Structure parent) {
parentStack.push(parent);
}
/**
* This method removes the structure from the top of the parent's stack.
*
* @return the structure that was removed from the stack
*/
public Structure popParent() {
try {
return parentStack.pop();
} catch (EmptyStackException e) {
return null;
}
}
/**
* This method retreives the structure at the top of the parent's stack but
* does not remove it.
*
* @return the structure from the top of the stack
*/
public Structure peekParent() {
try {
return parentStack.peek();
} catch (EmptyStackException e) {
return null;
}
}
/**
* This method adds a new modifier to the list.
*
* @param ownerOMA
* the owner's old memory address
* @param constraints
* the object's constraints
*/
public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
if (objectConstraints == null) {
objectConstraints = new ArrayList<Constraint>();
this.constraints.put(ownerOMA, objectConstraints);
}
objectConstraints.addAll(constraints);
}
/**
* Returns constraints applied to the feature of the given OMA.
* @param ownerOMA
* the constraints' owner OMA
* @return a list of constraints or <b>null</b> if no constraints are applied to the feature
*/
public List<Constraint> getConstraints(Long ownerOMA) {
return constraints.get(ownerOMA);
}
/**
* @return all available constraints
*/
public List<Constraint> getAllConstraints() {
List<Constraint> result = new ArrayList<Constraint>();
for (Entry<Long, List<Constraint>> entry : constraints.entrySet()) {
result.addAll(entry.getValue());
}
return result;
}
/**
* This method adds the animation for the specified OMA of its owner.
*
* @param ownerOMA
* the owner's old memory address
* @param animation
* the animation for the feature specified by ownerOMA
*/
public void addAnimation(Long ownerOMA, Animation animation) {
List<Animation> animList = animations.get(ownerOMA);
if (animList == null) {
animList = new ArrayList<Animation>();
animations.put(ownerOMA, animList);
}
animList.add(animation);
}
/**
* This method returns the animation data for the specified owner.
*
* @param ownerOMA
* the old memory address of the animation data owner
* @return the animation or null if none exists
*/
public List<Animation> getAnimations(Long ownerOMA) {
return animations.get(ownerOMA);
}
/**
* This method sets the skeleton for the specified OMA of its owner.
*
* @param skeletonOMA
* the skeleton's old memory address
* @param skeleton
* the skeleton specified by the given OMA
*/
public void setSkeleton(Long skeletonOMA, Skeleton skeleton) {
skeletons.put(skeletonOMA, skeleton);
}
/**
* The method stores a binding between the skeleton and the proper armature
* node.
*
* @param skeleton
* the skeleton
* @param node
* the armature node
*/
public void setNodeForSkeleton(Skeleton skeleton, Node node) {
nodesWithSkeletons.put(skeleton, node);
}
/**
* This method returns the armature node that is defined for the skeleton.
*
* @param skeleton
* the skeleton
* @return the armature node that defines the skeleton in blender
*/
public Node getControlledNode(Skeleton skeleton) {
return nodesWithSkeletons.get(skeleton);
}
/**
* This method returns the skeleton for the specified OMA of its owner.
*
* @param skeletonOMA
* the skeleton's old memory address
* @return the skeleton specified by the given OMA
*/
public Skeleton getSkeleton(Long skeletonOMA) {
return skeletons.get(skeletonOMA);
}
/**
* This method sets the bone context for the given bone old memory address.
* If the context is already set it will be replaced.
*
* @param boneOMA
* the bone's old memory address
* @param boneContext
* the bones's context
*/
public void setBoneContext(Long boneOMA, BoneContext boneContext) {
boneContexts.put(boneOMA, boneContext);
}
/**
* This method returns the bone context for the given bone old memory
* address. If no context exists then <b>null</b> is returned.
*
* @param boneOMA
* the bone's old memory address
* @return bone's context
*/
public BoneContext getBoneContext(Long boneOMA) {
return boneContexts.get(boneOMA);
}
/**
* Returns bone by given name.
*
* @param skeletonOMA
* the OMA of the skeleton where the bone will be searched
* @param name
* the name of the bone
* @return found bone or null if none bone of a given name exists
*/
public BoneContext getBoneByName(Long skeletonOMA, String name) {
for (Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
if (entry.getValue().getArmatureObjectOMA().equals(skeletonOMA)) {
Bone bone = entry.getValue().getBone();
if (bone != null && name.equals(bone.getName())) {
return entry.getValue();
}
}
}
return null;
}
/**
* Returns bone context for the given bone.
*
* @param bone
* the bone
* @return the bone's bone context
*/
public BoneContext getBoneContext(Bone bone) {
for (Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
if (entry.getValue().getBone().getName().equals(bone.getName())) {
return entry.getValue();
}
}
throw new IllegalStateException("Cannot find context for bone: " + bone);
}
/**
* This metod returns the default material.
*
* @return the default material
*/
public synchronized Material getDefaultMaterial() {
if (blenderKey.getDefaultMaterial() == null) {
Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
blenderKey.setDefaultMaterial(defaultMaterial);
}
return blenderKey.getDefaultMaterial();
}
/**
* Adds a custom marker for scene's feature.
*
* @param marker
* the marker name
* @param feature
* te scene's feature (can be node, material or texture or
* anything else)
* @param markerValue
* the marker value
*/
public void addMarker(String marker, Object feature, Object markerValue) {
if (markerValue == null) {
throw new IllegalArgumentException("The marker's value cannot be null.");
}
Map<Object, Object> markersMap = markers.get(marker);
if (markersMap == null) {
markersMap = new HashMap<Object, Object>();
markers.put(marker, markersMap);
}
markersMap.put(feature, markerValue);
}
/**
* Returns the marker value. The returned value is null if no marker was
* defined for the given feature.
*
* @param marker
* the marker name
* @param feature
* the scene's feature
* @return marker value or null if it was not defined
*/
public Object getMarkerValue(String marker, Object feature) {
Map<Object, Object> markersMap = markers.get(marker);
return markersMap == null ? null : markersMap.get(feature);
}
/**
* Adds blender action to the context.
* @param action
* the action loaded from the blend file
*/
public void addAction(BlenderAction action) {
actions.put(action.getName(), action);
}
/**
* @return a map of blender actions; the key is the action name and the value is action itself
*/
public Map<String, BlenderAction> getActions() {
return actions;
}
/**
* This enum defines what loaded data type user wants to retreive. It can be
* either filled structure or already converted data.
*
* @author Marcin Roguski (Kaelthas)
*/
public static enum LoadedDataType {
STRUCTURE, FEATURE, TEMPORAL_MESH;
}
@Override
public String toString() {
return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]";
}
}

@ -1,417 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene.plugins.blender;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetLocator;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.asset.ModelKey;
import com.jme3.asset.StreamAssetInfo;
import com.jme3.light.Light;
import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.LightNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.animations.AnimationHelper;
import com.jme3.scene.plugins.blender.cameras.CameraHelper;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.curves.CurvesHelper;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.landscape.LandscapeHelper;
import com.jme3.scene.plugins.blender.lights.LightHelper;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.scene.plugins.blender.meshes.MeshHelper;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
import com.jme3.scene.plugins.blender.textures.TextureHelper;
import com.jme3.texture.Texture;
/**
* This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures.
* @author Marcin Roguski (Kaelthas)
*/
public class BlenderLoader implements AssetLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
@Override
public Spatial load(AssetInfo assetInfo) throws IOException {
try {
BlenderContext blenderContext = this.setup(assetInfo);
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blenderContext.getBlocks()) {
switch (block.getCode()) {
case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node object = (Node) objectHelper.toObject(block.getStructure(blenderContext), blenderContext);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() });
}
if (object.getParent() == null) {
loadedFeatures.objects.add(object);
}
if (object instanceof LightNode && ((LightNode) object).getLight() != null) {
loadedFeatures.lights.add(((LightNode) object).getLight());
} else if (object instanceof CameraNode && ((CameraNode) object).getCamera() != null) {
loadedFeatures.cameras.add(((CameraNode) object).getCamera());
}
break;
case BLOCK_SC00:// Scene
loadedFeatures.sceneBlocks.add(block);
break;
case BLOCK_MA00:// Material
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
loadedFeatures.materials.add(materialContext);
break;
case BLOCK_ME00:// Mesh
MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
TemporalMesh temporalMesh = meshHelper.toTemporalMesh(block.getStructure(blenderContext), blenderContext);
loadedFeatures.meshes.add(temporalMesh);
break;
case BLOCK_IM00:// Image
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
Texture image = textureHelper.loadImageAsTexture(block.getStructure(blenderContext), 0, blenderContext);
if (image != null && image.getImage() != null) {// render results are stored as images but are not being loaded
loadedFeatures.images.add(image);
}
break;
case BLOCK_TE00:
Structure textureStructure = block.getStructure(blenderContext);
int type = ((Number) textureStructure.getFieldValue("type")).intValue();
if (type == TextureHelper.TEX_IMAGE) {
TextureHelper texHelper = blenderContext.getHelper(TextureHelper.class);
Texture texture = texHelper.getTexture(textureStructure, null, blenderContext);
if (texture != null) {// null is returned when texture has no image
loadedFeatures.textures.add(texture);
}
} else {
LOGGER.fine("Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object.");
}
break;
case BLOCK_WO00:// World
LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class);
Structure worldStructure = block.getStructure(blenderContext);
String worldName = worldStructure.getName();
if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
Light ambientLight = landscapeHelper.toAmbientLight(worldStructure);
if (ambientLight != null) {
loadedFeatures.objects.add(new LightNode(null, ambientLight));
loadedFeatures.lights.add(ambientLight);
}
loadedFeatures.sky = landscapeHelper.toSky(worldStructure);
loadedFeatures.backgroundColor = landscapeHelper.toBackgroundColor(worldStructure);
Filter fogFilter = landscapeHelper.toFog(worldStructure);
if (fogFilter != null) {
loadedFeatures.filters.add(landscapeHelper.toFog(worldStructure));
}
}
break;
case BLOCK_AC00:
LOGGER.fine("Loading unlinked animations is not yet supported!");
break;
default:
LOGGER.log(Level.FINEST, "Ommiting the block: {0}.", block.getCode());
}
}
LOGGER.fine("Baking constraints after every feature is loaded.");
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.bakeConstraints(blenderContext);
LOGGER.fine("Loading scenes and attaching them to the root object.");
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext));
}
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
Node modelRoot = new Node(blenderKey.getName());
for (Node scene : loadedFeatures.scenes) {
modelRoot.attachChild(scene);
}
if (blenderKey.isLoadUnlinkedAssets()) {
LOGGER.fine("Setting loaded content as user data in resulting sptaial.");
Map<String, Map<String, Object>> linkedData = new HashMap<String, Map<String, Object>>();
Map<String, Object> thisFileData = new HashMap<String, Object>();
thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList<Object>() : loadedFeatures.scenes);
thisFileData.put("objects", loadedFeatures.objects == null ? new ArrayList<Object>() : loadedFeatures.objects);
thisFileData.put("meshes", loadedFeatures.meshes == null ? new ArrayList<Object>() : loadedFeatures.meshes);
thisFileData.put("materials", loadedFeatures.materials == null ? new ArrayList<Object>() : loadedFeatures.materials);
thisFileData.put("textures", loadedFeatures.textures == null ? new ArrayList<Object>() : loadedFeatures.textures);
thisFileData.put("images", loadedFeatures.images == null ? new ArrayList<Object>() : loadedFeatures.images);
thisFileData.put("animations", loadedFeatures.animations == null ? new ArrayList<Object>() : loadedFeatures.animations);
thisFileData.put("cameras", loadedFeatures.cameras == null ? new ArrayList<Object>() : loadedFeatures.cameras);
thisFileData.put("lights", loadedFeatures.lights == null ? new ArrayList<Object>() : loadedFeatures.lights);
thisFileData.put("filters", loadedFeatures.filters == null ? new ArrayList<Object>() : loadedFeatures.filters);
thisFileData.put("backgroundColor", loadedFeatures.backgroundColor);
thisFileData.put("sky", loadedFeatures.sky);
linkedData.put("this", thisFileData);
linkedData.putAll(blenderContext.getLinkedFeatures());
modelRoot.setUserData("linkedData", linkedData);
}
return modelRoot;
} catch (BlenderFileException e) {
throw new IOException(e.getLocalizedMessage(), e);
} catch (Exception e) {
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
} finally {
this.clear(assetInfo);
}
}
/**
* This method converts the given structure to a scene node.
* @param structure
* structure of a scene
* @param blenderContext the blender context
* @return scene's node
* @throws BlenderFileException
* an exception throw when problems with blender file occur
*/
private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node result = new Node(structure.getName());
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
for (Structure b : base) {
Pointer pObject = (Pointer) b.getFieldValue("object");
if (pObject.isNotNull()) {
Structure objectStructure = pObject.fetchData().get(0);
Object object = objectHelper.toObject(objectStructure, blenderContext);
if (object instanceof LightNode) {
result.addLight(((LightNode) object).getLight());// FIXME: check if this is needed !!!
result.attachChild((LightNode) object);
} else if (object instanceof Node) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() });
}
if (((Node) object).getParent() == null) {
result.attachChild((Spatial) object);
}
}
}
}
return result;
}
/**
* This method sets up the loader.
* @param assetInfo
* the asset info
* @throws BlenderFileException
* an exception is throw when something wrong happens with blender file
*/
protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException {
// registering loaders
ModelKey modelKey = (ModelKey) assetInfo.getKey();
BlenderKey blenderKey;
if (modelKey instanceof BlenderKey) {
blenderKey = (BlenderKey) modelKey;
} else {
blenderKey = new BlenderKey(modelKey.getName());
}
// opening stream
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
// reading blocks
List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
FileBlockHeader fileBlock;
BlenderContext blenderContext = new BlenderContext();
blenderContext.setBlenderVersion(inputStream.getVersionNumber());
blenderContext.setAssetManager(assetInfo.getManager());
blenderContext.setInputStream(inputStream);
blenderContext.setBlenderKey(blenderKey);
// creating helpers
blenderContext.putHelper(AnimationHelper.class, new AnimationHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext));
// reading the blocks (dna block is automatically saved in the blender context when found)
FileBlockHeader sceneFileBlock = null;
do {
fileBlock = new FileBlockHeader(inputStream, blenderContext);
if (!fileBlock.isDnaBlock()) {
blocks.add(fileBlock);
// save the scene's file block
if (fileBlock.getCode() == BlockCode.BLOCK_SC00) {
sceneFileBlock = fileBlock;
}
}
} while (!fileBlock.isLastBlock());
if (sceneFileBlock != null) {
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
}
// adding locator for linked content
assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
return blenderContext;
}
/**
* The internal data is only needed during loading so make it unreachable so that the GC can release
* that memory (which can be quite large amount).
*/
protected void clear(AssetInfo assetInfo) {
assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
}
/**
* This class holds the loading results according to the given loading flag.
* @author Marcin Roguski (Kaelthas)
*/
private static class LoadedFeatures {
private List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
/** The scenes from the file. */
private List<Node> scenes = new ArrayList<Node>();
/** Objects from all scenes. */
private List<Node> objects = new ArrayList<Node>();
/** All meshes. */
private List<TemporalMesh> meshes = new ArrayList<TemporalMesh>();
/** Materials from all objects. */
private List<MaterialContext> materials = new ArrayList<MaterialContext>();
/** Textures from all objects. */
private List<Texture> textures = new ArrayList<Texture>();
/** The images stored in the blender file. */
private List<Texture> images = new ArrayList<Texture>();
/** Animations of all objects. */
private List<Animation> animations = new ArrayList<Animation>();
/** All cameras from the file. */
private List<Camera> cameras = new ArrayList<Camera>();
/** All lights from the file. */
private List<Light> lights = new ArrayList<Light>();
/** Loaded sky. */
private Spatial sky;
/** Scene filters (ie. FOG). */
private List<Filter> filters = new ArrayList<Filter>();
/**
* The background color of the render loaded from the horizon color of the world. If no world is used than the gray color
* is set to default (as in blender editor.
*/
private ColorRGBA backgroundColor = ColorRGBA.Gray;
}
public static class LinkedContentLocator implements AssetLocator {
private File rootFolder;
@Override
public void setRootPath(String rootPath) {
rootFolder = new File(rootPath);
if(rootFolder.isFile()) {
rootFolder = rootFolder.getParentFile();
}
}
@SuppressWarnings("rawtypes")
@Override
public AssetInfo locate(AssetManager manager, AssetKey key) {
if(key instanceof BlenderKey) {
File linkedAbsoluteFile = new File(key.getName());
if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName());
if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName());
if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder));
} catch (FileNotFoundException e) {
return null;
}
}
}
return null;
}
}
}

@ -1,391 +0,0 @@
package com.jme3.scene.plugins.blender.animations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl;
import com.jme3.animation.SpatialTrack;
import com.jme3.asset.BlenderKey.AnimationMatchMethod;
import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.Ipo.ConstIpo;
import com.jme3.scene.plugins.blender.curves.BezierCurve;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/**
* The helper class that helps in animations loading.
* @author Marcin Roguski (Kaelthas)
*/
public class AnimationHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(AnimationHelper.class.getName());
public AnimationHelper(String blenderVersion, BlenderContext blenderContext) {
super(blenderVersion, blenderContext);
}
/**
* Loads all animations that are stored in the blender file. The animations are not yet applied to the scene features.
* This should be called before objects are loaded.
* @throws BlenderFileException
* an exception is thrown when problems with blender file reading occur
*/
public void loadAnimations() throws BlenderFileException {
LOGGER.info("Loading animations that will be later applied to scene features.");
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(BlockCode.BLOCK_AC00);
if (actionHeaders != null) {
for (FileBlockHeader header : actionHeaders) {
Structure actionStructure = header.getStructure(blenderContext);
LOGGER.log(Level.INFO, "Found animation: {0}.", actionStructure.getName());
blenderContext.addAction(this.getTracks(actionStructure, blenderContext));
}
}
}
/**
* The method applies animations to the given node. The names of the animations should be the same as actions names in the blender file.
* @param node
* the node to whom the animations will be applied
* @param animationMatchMethod
* the way animation should be matched with node
*/
public void applyAnimations(Node node, AnimationMatchMethod animationMatchMethod) {
List<BlenderAction> actions = this.getActions(node, animationMatchMethod);
if (actions.size() > 0) {
List<Animation> animations = new ArrayList<Animation>();
for (BlenderAction action : actions) {
SpatialTrack[] tracks = action.toTracks(node, blenderContext);
if (tracks != null && tracks.length > 0) {
Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime());
spatialAnimation.setTracks(tracks);
animations.add(spatialAnimation);
blenderContext.addAnimation((Long) node.getUserData(ObjectHelper.OMA_MARKER), spatialAnimation);
}
}
if (animations.size() > 0) {
AnimControl control = new AnimControl();
HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size());
for (int i = 0; i < animations.size(); ++i) {
Animation animation = animations.get(i);
anims.put(animation.getName(), animation);
}
control.setAnimations(anims);
node.addControl(control);
}
}
}
/**
* The method applies skeleton animations to the given node.
* @param node
* the node where the animations will be applied
* @param skeleton
* the skeleton of the node
* @param animationMatchMethod
* the way animation should be matched with skeleton
*/
public void applyAnimations(Node node, Skeleton skeleton, AnimationMatchMethod animationMatchMethod) {
node.addControl(new SkeletonControl(skeleton));
blenderContext.setNodeForSkeleton(skeleton, node);
List<BlenderAction> actions = this.getActions(skeleton, animationMatchMethod);
if (actions.size() > 0) {
List<Animation> animations = new ArrayList<Animation>();
for (BlenderAction action : actions) {
BoneTrack[] tracks = action.toTracks(skeleton, blenderContext);
if (tracks != null && tracks.length > 0) {
Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime());
boneAnimation.setTracks(tracks);
animations.add(boneAnimation);
Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
blenderContext.addAnimation(animatedNodeOMA, boneAnimation);
}
}
if (animations.size() > 0) {
AnimControl control = new AnimControl(skeleton);
HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size());
for (int i = 0; i < animations.size(); ++i) {
Animation animation = animations.get(i);
anims.put(animation.getName(), animation);
}
control.setAnimations(anims);
node.addControl(control);
// make sure that SkeletonControl is added AFTER the AnimControl
SkeletonControl skeletonControl = node.getControl(SkeletonControl.class);
if (skeletonControl != null) {
node.removeControl(SkeletonControl.class);
node.addControl(skeletonControl);
}
}
}
}
/**
* This method creates an ipo object used for interpolation calculations.
*
* @param ipoStructure
* the structure with ipo definition
* @param blenderContext
* the blender context
* @return the ipo object
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
// preparing bezier curves
Ipo result = null;
List<Structure> curves = curvebase.evaluateListBase();// IpoCurve
if (curves.size() > 0) {
BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
int frame = 0;
for (Structure curve : curves) {
Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
List<Structure> bezTriples = pBezTriple.fetchData();
int type = ((Number) curve.getFieldValue("adrcode")).intValue();
bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
}
curves.clear();
result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
Long ipoOma = ipoStructure.getOldMemoryAddress();
blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.STRUCTURE, ipoStructure);
blenderContext.addLoadedFeatures(ipoOma, LoadedDataType.FEATURE, result);
}
return result;
}
/**
* This method creates an ipo with only a single value. No track type is
* specified so do not use it for calculating tracks.
*
* @param constValue
* the value of this ipo
* @return constant ipo
*/
public Ipo fromValue(float constValue) {
return new ConstIpo(constValue);
}
/**
* This method retuns the bone tracks for animation.
*
* @param actionStructure
* the structure containing the tracks
* @param blenderContext
* the blender context
* @return a list of tracks for the specified animation
* @throws BlenderFileException
* an exception is thrown when there are problems with the blend
* file
*/
private BlenderAction getTracks(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion < 250) {
return this.getTracks249(actionStructure, blenderContext);
} else {
return this.getTracks250(actionStructure, blenderContext);
}
}
/**
* This method retuns the bone tracks for animation for blender version 2.50
* and higher.
*
* @param actionStructure
* the structure containing the tracks
* @param blenderContext
* the blender context
* @return a list of tracks for the specified animation
* @throws BlenderFileException
* an exception is thrown when there are problems with the blend
* file
*/
private BlenderAction getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Getting tracks!");
Structure groups = (Structure) actionStructure.getFieldValue("groups");
List<Structure> actionGroups = groups.evaluateListBase();// bActionGroup
BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps());
int lastFrame = 1;
for (Structure actionGroup : actionGroups) {
String name = actionGroup.getFieldValue("name").toString();
List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase();
BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
int channelCounter = 0;
for (Structure c : channels) {
int type = this.getCurveType(c, blenderContext);
Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
List<Structure> bezTriples = pBezTriple.fetchData();
bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
}
Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
lastFrame = Math.max(lastFrame, ipo.getLastFrame());
blenderAction.featuresTracks.put(name, ipo);
}
blenderAction.stopFrame = lastFrame;
return blenderAction;
}
/**
* This method retuns the bone tracks for animation for blender version 2.49
* (and probably several lower versions too).
*
* @param actionStructure
* the structure containing the tracks
* @param blenderContext
* the blender context
* @return a list of tracks for the specified animation
* @throws BlenderFileException
* an exception is thrown when there are problems with the blend
* file
*/
private BlenderAction getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Getting tracks!");
Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
List<Structure> actionChannels = chanbase.evaluateListBase();// bActionChannel
BlenderAction blenderAction = new BlenderAction(actionStructure.getName(), blenderContext.getBlenderKey().getFps());
int lastFrame = 1;
for (Structure bActionChannel : actionChannels) {
String animatedFeatureName = bActionChannel.getFieldValue("name").toString();
Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
if (!p.isNull()) {
Structure ipoStructure = p.fetchData().get(0);
Ipo ipo = this.fromIpoStructure(ipoStructure, blenderContext);
if (ipo != null) {// this can happen when ipo with no curves appear in blender file
lastFrame = Math.max(lastFrame, ipo.getLastFrame());
blenderAction.featuresTracks.put(animatedFeatureName, ipo);
}
}
}
blenderAction.stopFrame = lastFrame;
return blenderAction;
}
/**
* This method returns the type of the ipo curve.
*
* @param structure
* the structure must contain the 'rna_path' field and
* 'array_index' field (the type is not important here)
* @param blenderContext
* the blender context
* @return the type of the curve
*/
public int getCurveType(Structure structure, BlenderContext blenderContext) {
// reading rna path first
BlenderInputStream bis = blenderContext.getInputStream();
int currentPosition = bis.getPosition();
Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
bis.setPosition(dataFileBlock.getBlockPosition());
String rnaPath = bis.readString();
bis.setPosition(currentPosition);
int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
// determining the curve type
if (rnaPath.endsWith("location")) {
return Ipo.AC_LOC_X + arrayIndex;
}
if (rnaPath.endsWith("rotation_quaternion")) {
return Ipo.AC_QUAT_W + arrayIndex;
}
if (rnaPath.endsWith("scale")) {
return Ipo.AC_SIZE_X + arrayIndex;
}
if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
return Ipo.OB_ROT_X + arrayIndex;
}
LOGGER.log(Level.WARNING, "Unknown curve rna path: {0}", rnaPath);
return -1;
}
/**
* The method returns the actions for the given skeleton. The actions represent armature animation in blender.
* @param skeleton
* the skeleton we fetch the actions for
* @param animationMatchMethod
* the method of animation matching
* @return a list of animations for the specified skeleton
*/
private List<BlenderAction> getActions(Skeleton skeleton, AnimationMatchMethod animationMatchMethod) {
List<BlenderAction> result = new ArrayList<BlenderAction>();
// first get a set of bone names
Set<String> boneNames = new HashSet<String>();
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
String boneName = skeleton.getBone(i).getName();
if (boneName != null && boneName.length() > 0) {
boneNames.add(skeleton.getBone(i).getName());
}
}
// finding matches
Set<String> matchingNames = new HashSet<String>();
for (Entry<String, BlenderAction> actionEntry : blenderContext.getActions().entrySet()) {
// compute how many action tracks match the skeleton bones' names
for (String boneName : boneNames) {
if (actionEntry.getValue().hasTrackName(boneName)) {
matchingNames.add(boneName);
}
}
BlenderAction action = null;
if (animationMatchMethod == AnimationMatchMethod.AT_LEAST_ONE_NAME_MATCH && matchingNames.size() > 0) {
action = actionEntry.getValue();
} else if (matchingNames.size() == actionEntry.getValue().getTracksCount()) {
action = actionEntry.getValue();
}
if (action != null) {
// remove the tracks that do not match the bone names if the matching method is different from ALL_NAMES_MATCH
if (animationMatchMethod != AnimationMatchMethod.ALL_NAMES_MATCH) {
action = action.clone();
action.removeTracksThatAreNotInTheCollection(matchingNames);
}
result.add(action);
}
matchingNames.clear();
}
return result;
}
/**
* The method returns the actions for the given node. The actions represent object animation in blender.
* @param node
* the node we fetch the actions for
* @param animationMatchMethod
* the method of animation matching
* @return a list of animations for the specified node
*/
private List<BlenderAction> getActions(Node node, AnimationMatchMethod animationMatchMethod) {
List<BlenderAction> result = new ArrayList<BlenderAction>();
for (Entry<String, BlenderAction> actionEntry : blenderContext.getActions().entrySet()) {
if (actionEntry.getValue().hasTrackName(node.getName())) {
result.add(actionEntry.getValue());
}
}
return result;
}
}

@ -1,134 +0,0 @@
package com.jme3.scene.plugins.blender.animations;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SpatialTrack;
import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.BlenderContext;
/**
* An abstract representation of animation. The data stored here is mainly a
* raw action data loaded from blender. It can later be transformed into
* bone or spatial animation and applied to the specified node.
*
* @author Marcin Roguski (Kaelthas)
*/
public class BlenderAction implements Cloneable {
/** The action name. */
/* package */final String name;
/** Animation speed - frames per second. */
/* package */int fps;
/**
* The last frame of the animation (the last ipo curve node position is
* used as a last frame).
*/
/* package */int stopFrame;
/**
* Tracks of the features. In case of bone animation the keys are the
* names of the bones. In case of spatial animation - the node's name is
* used. A single ipo contains all tracks for location, rotation and
* scales.
*/
/* package */Map<String, Ipo> featuresTracks = new HashMap<String, Ipo>();
public BlenderAction(String name, int fps) {
this.name = name;
this.fps = fps;
}
public void removeTracksThatAreNotInTheCollection(Collection<String> trackNames) {
Map<String, Ipo> newTracks = new HashMap<String, Ipo>();
for (String trackName : trackNames) {
if (featuresTracks.containsKey(trackName)) {
newTracks.put(trackName, featuresTracks.get(trackName));
}
}
featuresTracks = newTracks;
}
@Override
public BlenderAction clone() {
BlenderAction result = new BlenderAction(name, fps);
result.stopFrame = stopFrame;
result.featuresTracks = new HashMap<String, Ipo>(featuresTracks);
return result;
}
/**
* Converts the action into JME spatial animation tracks.
*
* @param node
* the node that will be animated
* @return the spatial tracks for the node
*/
public SpatialTrack[] toTracks(Node node, BlenderContext blenderContext) {
List<SpatialTrack> tracks = new ArrayList<SpatialTrack>(featuresTracks.size());
for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
tracks.add((SpatialTrack) entry.getValue().calculateTrack(0, null, node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale(), 1, stopFrame, fps, true));
}
return tracks.toArray(new SpatialTrack[tracks.size()]);
}
/**
* Converts the action into JME bone animation tracks.
*
* @param skeleton
* the skeleton that will be animated
* @return the bone tracks for the node
*/
public BoneTrack[] toTracks(Skeleton skeleton, BlenderContext blenderContext) {
List<BoneTrack> tracks = new ArrayList<BoneTrack>(featuresTracks.size());
for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
int boneIndex = skeleton.getBoneIndex(entry.getKey());
BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(boneIndex));
tracks.add((BoneTrack) entry.getValue().calculateTrack(boneIndex, boneContext, boneContext.getBone().getBindPosition(), boneContext.getBone().getBindRotation(), boneContext.getBone().getBindScale(), 1, stopFrame, fps, false));
}
return tracks.toArray(new BoneTrack[tracks.size()]);
}
/**
* @return the name of the action
*/
public String getName() {
return name;
}
/**
* @return the time of animations (in seconds)
*/
public float getAnimationTime() {
return (stopFrame - 1) / (float) fps;
}
/**
* Determines if the current action has a track of a given name.
* CAUTION! The names are case sensitive.
*
* @param name
* the name of the track
* @return <B>true</b> if the track of a given name exists for the
* action and <b>false</b> otherwise
*/
public boolean hasTrackName(String name) {
return featuresTracks.containsKey(name);
}
/**
* @return the amount of tracks in current action
*/
public int getTracksCount() {
return featuresTracks.size();
}
@Override
public String toString() {
return "BlenderTrack [name = " + name + "; tracks = [" + featuresTracks.keySet() + "]]";
}
}

@ -1,400 +0,0 @@
package com.jme3.scene.plugins.blender.animations;
import java.util.ArrayList;
import java.util.List;
import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/**
* This class holds the basic data that describes a bone.
*
* @author Marcin Roguski (Kaelthas)
*/
public class BoneContext {
// the flags of the bone
public static final int SELECTED = 0x000001;
public static final int CONNECTED_TO_PARENT = 0x000010;
public static final int DEFORM = 0x001000;
public static final int NO_LOCAL_LOCATION = 0x400000;
public static final int NO_INHERIT_SCALE = 0x008000;
public static final int NO_INHERIT_ROTATION = 0x000200;
/**
* The bones' matrices have, unlike objects', the coordinate system identical to JME's (Y axis is UP, X to the right and Z toward us).
* So in order to have them loaded properly we need to transform their armature matrix (which blender sees as rotated) to make sure we get identical results.
*/
public static final Matrix4f BONE_ARMATURE_TRANSFORMATION_MATRIX = new Matrix4f(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
private static final int IKFLAG_LOCK_X = 0x01;
private static final int IKFLAG_LOCK_Y = 0x02;
private static final int IKFLAG_LOCK_Z = 0x04;
private static final int IKFLAG_LIMIT_X = 0x08;
private static final int IKFLAG_LIMIT_Y = 0x10;
private static final int IKFLAG_LIMIT_Z = 0x20;
private BlenderContext blenderContext;
/** The OMA of the bone's armature object. */
private Long armatureObjectOMA;
/** The OMA of the model that owns the bone's skeleton. */
private Long skeletonOwnerOma;
/** The structure of the bone. */
private Structure boneStructure;
/** Bone's name. */
private String boneName;
/** The bone's flag. */
private int flag;
/** The bone's matrix in world space. */
private Matrix4f globalBoneMatrix;
/** The bone's matrix in the model space. */
private Matrix4f boneMatrixInModelSpace;
/** The parent context. */
private BoneContext parent;
/** The children of this context. */
private List<BoneContext> children = new ArrayList<BoneContext>();
/** Created bone (available after calling 'buildBone' method). */
private Bone bone;
/** The length of the bone. */
private float length;
/** The bone's deform envelope. */
private BoneEnvelope boneEnvelope;
// The below data is used only for IK constraint computations.
/** The bone's stretch value. */
private float ikStretch;
/** Bone's rotation minimum values. */
private Vector3f limitMin;
/** Bone's rotation maximum values. */
private Vector3f limitMax;
/** The bone's stiffness values (how much it rotates during IK computations. */
private Vector3f stiffness;
/** Values that indicate if any axis' rotation should be limited by some angle. */
private boolean[] limits;
/** Values that indicate if any axis' rotation should be disabled during IK computations. */
private boolean[] locks;
/**
* Constructor. Creates the basic set of bone's data.
*
* @param armatureObjectOMA
* the OMA of the bone's armature object
* @param boneStructure
* the bone's structure
* @param blenderContext
* the blender context
* @throws BlenderFileException
* an exception is thrown when problem with blender data reading
* occurs
*/
public BoneContext(Long armatureObjectOMA, Structure boneStructure, BlenderContext blenderContext) throws BlenderFileException {
this(boneStructure, armatureObjectOMA, null, blenderContext);
}
/**
* Constructor. Creates the basic set of bone's data.
*
* @param boneStructure
* the bone's structure
* @param armatureObjectOMA
* the OMA of the bone's armature object
* @param parent
* bone's parent (null if the bone is the root bone)
* @param blenderContext
* the blender context
* @throws BlenderFileException
* an exception is thrown when problem with blender data reading
* occurs
*/
@SuppressWarnings("unchecked")
private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException {
this.parent = parent;
this.blenderContext = blenderContext;
this.boneStructure = boneStructure;
this.armatureObjectOMA = armatureObjectOMA;
boneName = boneStructure.getFieldValue("name").toString();
flag = ((Number) boneStructure.getFieldValue("flag")).intValue();
length = ((Number) boneStructure.getFieldValue("length")).floatValue();
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
// first get the bone matrix in its armature space
globalBoneMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
if (blenderContext.getBlenderKey().isFixUpAxis()) {
// then make sure it is rotated in a proper way to fit the jme bone transformation conventions
globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
}
Structure armatureStructure = blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext);
Spatial armature = (Spatial) objectHelper.toObject(armatureStructure, blenderContext);
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform(), new Matrix4f());
// and now compute the final bone matrix in world space
globalBoneMatrix = armatureWorldMatrix.mult(globalBoneMatrix);
// load the bone deformation envelope if necessary
if ((flag & DEFORM) == 0) {// if the flag is NOT set then the DEFORM is in use
boneEnvelope = new BoneEnvelope(boneStructure, armatureWorldMatrix, blenderContext.getBlenderKey().isFixUpAxis());
}
// load bone's pose channel data
Pointer pPose = (Pointer) armatureStructure.getFieldValue("pose");
if (pPose != null && pPose.isNotNull()) {
List<Structure> poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
for (Structure poseChannel : poseChannels) {
Long boneOMA = ((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress();
if (boneOMA.equals(this.boneStructure.getOldMemoryAddress())) {
ikStretch = ((Number) poseChannel.getFieldValue("ikstretch")).floatValue();
DynamicArray<Number> limitMin = (DynamicArray<Number>) poseChannel.getFieldValue("limitmin");
this.limitMin = new Vector3f(limitMin.get(0).floatValue(), limitMin.get(1).floatValue(), limitMin.get(2).floatValue());
DynamicArray<Number> limitMax = (DynamicArray<Number>) poseChannel.getFieldValue("limitmax");
this.limitMax = new Vector3f(limitMax.get(0).floatValue(), limitMax.get(1).floatValue(), limitMax.get(2).floatValue());
DynamicArray<Number> stiffness = (DynamicArray<Number>) poseChannel.getFieldValue("stiffness");
this.stiffness = new Vector3f(stiffness.get(0).floatValue(), stiffness.get(1).floatValue(), stiffness.get(2).floatValue());
int ikFlag = ((Number) poseChannel.getFieldValue("ikflag")).intValue();
locks = new boolean[] { (ikFlag & IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LOCK_Z) != 0 };
// limits are enabled when locks are disabled, so we ween to take that into account here
limits = new boolean[] { (ikFlag & IKFLAG_LIMIT_X & ~IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LIMIT_Y & ~IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LIMIT_Z & ~IKFLAG_LOCK_Z) != 0 };
break;// we have found what we need, no need to search further
}
}
}
// create the children
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase();
for (Structure child : childbase) {
children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext));
}
blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
}
/**
* This method builds the bone. It recursively builds the bone's children.
*
* @param bones
* a list of bones where the newly created bone will be added
* @param skeletonOwnerOma
* the spatial of the object that will own the skeleton
* @param blenderContext
* the blender context
* @return newly created bone
*/
public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
this.skeletonOwnerOma = skeletonOwnerOma;
Long boneOMA = boneStructure.getOldMemoryAddress();
bone = new Bone(boneName);
bones.add(bone);
blenderContext.addLoadedFeatures(boneOMA, LoadedDataType.STRUCTURE, boneStructure);
blenderContext.addLoadedFeatures(boneOMA, LoadedDataType.FEATURE, bone);
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Structure skeletonOwnerObjectStructure = (Structure) blenderContext.getLoadedFeature(skeletonOwnerOma, LoadedDataType.STRUCTURE);
// I could load 'imat' here, but apparently in some older blenders there were bugs or unfinished functionalities that stored ZERO matrix in imat field
// loading 'obmat' and inverting it makes us avoid errors in such cases
Matrix4f invertedObjectOwnerGlobalMatrix = objectHelper.getMatrix(skeletonOwnerObjectStructure, "obmat", blenderContext.getBlenderKey().isFixUpAxis()).invertLocal();
if (objectHelper.isParent(skeletonOwnerOma, armatureObjectOMA)) {
boneMatrixInModelSpace = globalBoneMatrix.mult(invertedObjectOwnerGlobalMatrix);
} else {
boneMatrixInModelSpace = invertedObjectOwnerGlobalMatrix.mult(globalBoneMatrix);
}
Matrix4f boneLocalMatrix = parent == null ? boneMatrixInModelSpace : parent.boneMatrixInModelSpace.invert().multLocal(boneMatrixInModelSpace);
Vector3f poseLocation = parent == null || !this.is(CONNECTED_TO_PARENT) ? boneLocalMatrix.toTranslationVector() : new Vector3f(0, parent.length, 0);
Quaternion rotation = boneLocalMatrix.toRotationQuat().normalizeLocal();
Vector3f scale = boneLocalMatrix.toScaleVector();
bone.setBindTransforms(poseLocation, rotation, scale);
for (BoneContext child : children) {
bone.addChild(child.buildBone(bones, skeletonOwnerOma, blenderContext));
}
return bone;
}
/**
* @return built bone (available after calling 'buildBone' method)
*/
public Bone getBone() {
return bone;
}
/**
* @return the old memory address of the bone
*/
public Long getBoneOma() {
return boneStructure.getOldMemoryAddress();
}
/**
* The method returns the length of the bone.
* If you want to use it for bone debugger take model space scale into account and do
* something like this:
* <b>boneContext.getLength() * boneContext.getBone().getModelSpaceScale().y</b>.
* Otherwise the bones might not look as they should in the bone debugger.
* @return the length of the bone
*/
public float getLength() {
return length;
}
/**
* @return OMA of the bone's armature object
*/
public Long getArmatureObjectOMA() {
return armatureObjectOMA;
}
/**
* @return the OMA of the model that owns the bone's skeleton
*/
public Long getSkeletonOwnerOma() {
return skeletonOwnerOma;
}
/**
* @return the skeleton the bone of this context belongs to
*/
public Skeleton getSkeleton() {
return blenderContext.getSkeleton(armatureObjectOMA);
}
/**
* @return the initial bone's matrix in model space
*/
public Matrix4f getBoneMatrixInModelSpace() {
return boneMatrixInModelSpace;
}
/**
* @return the vertex assigning envelope of the bone
*/
public BoneEnvelope getBoneEnvelope() {
return boneEnvelope;
}
/**
* @return bone's stretch factor
*/
public float getIkStretch() {
return ikStretch;
}
/**
* @return indicates if the X rotation should be limited
*/
public boolean isLimitX() {
return limits != null ? limits[0] : false;
}
/**
* @return indicates if the Y rotation should be limited
*/
public boolean isLimitY() {
return limits != null ? limits[1] : false;
}
/**
* @return indicates if the Z rotation should be limited
*/
public boolean isLimitZ() {
return limits != null ? limits[2] : false;
}
/**
* @return indicates if the X rotation should be disabled
*/
public boolean isLockX() {
return locks != null ? locks[0] : false;
}
/**
* @return indicates if the Y rotation should be disabled
*/
public boolean isLockY() {
return locks != null ? locks[1] : false;
}
/**
* @return indicates if the Z rotation should be disabled
*/
public boolean isLockZ() {
return locks != null ? locks[2] : false;
}
/**
* @return the minimum values in rotation limitation (if limitation is enabled for specific axis).
*/
public Vector3f getLimitMin() {
return limitMin;
}
/**
* @return the maximum values in rotation limitation (if limitation is enabled for specific axis).
*/
public Vector3f getLimitMax() {
return limitMax;
}
/**
* @return the stiffness of the bone
*/
public Vector3f getStiffness() {
return stiffness;
}
/**
* Tells if the bone is of specified property defined by its flag.
* @param flagMask
* the mask of the flag (constants defined in this class)
* @return <b>true</b> if the bone IS of specified proeprty and <b>false</b> otherwise
*/
public boolean is(int flagMask) {
return (flag & flagMask) != 0;
}
/**
* @return the root bone context of this bone context
*/
public BoneContext getRoot() {
BoneContext result = this;
while (result.parent != null) {
result = result.parent;
}
return result;
}
/**
* @return a number of bones from this bone to its root
*/
public int getDistanceFromRoot() {
int result = 0;
BoneContext boneContext = this;
while (boneContext.parent != null) {
boneContext = boneContext.parent;
++result;
}
return result;
}
@Override
public String toString() {
return "BoneContext: " + boneName;
}
}

@ -1,133 +0,0 @@
package com.jme3.scene.plugins.blender.animations;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* An implementation of bone envelope. Used when assigning bones to the mesh by envelopes.
*
* @author Marcin Roguski
*/
public class BoneEnvelope {
/** A defined distance that will be included in the envelope space. */
private float distance;
/** The bone's weight. */
private float weight;
/** The radius of the bone's head. */
private float boneHeadRadius;
/** The radius of the bone's tail. */
private float boneTailRadius;
/** Head position in rest pose in world space. */
private Vector3f head;
/** Tail position in rest pose in world space. */
private Vector3f tail;
/**
* The constructor of bone envelope. It reads all the needed data. Take notice that the positions of head and tail
* are computed in the world space and that the points' positions given for computations should be in world space as well.
*
* @param boneStructure
* the blender bone structure
* @param armatureWorldMatrix
* the world matrix of the armature object
* @param fixUpAxis
* a variable that tells if we use the Y-is up axis orientation
*/
@SuppressWarnings("unchecked")
public BoneEnvelope(Structure boneStructure, Matrix4f armatureWorldMatrix, boolean fixUpAxis) {
distance = ((Number) boneStructure.getFieldValue("dist")).floatValue();
weight = ((Number) boneStructure.getFieldValue("weight")).floatValue();
boneHeadRadius = ((Number) boneStructure.getFieldValue("rad_head")).floatValue();
boneTailRadius = ((Number) boneStructure.getFieldValue("rad_tail")).floatValue();
DynamicArray<Number> headArray = (DynamicArray<Number>) boneStructure.getFieldValue("arm_head");
head = new Vector3f(headArray.get(0).floatValue(), headArray.get(1).floatValue(), headArray.get(2).floatValue());
if (fixUpAxis) {
float z = head.z;
head.z = -head.y;
head.y = z;
}
armatureWorldMatrix.mult(head, head);// move the head point to global space
DynamicArray<Number> tailArray = (DynamicArray<Number>) boneStructure.getFieldValue("arm_tail");
tail = new Vector3f(tailArray.get(0).floatValue(), tailArray.get(1).floatValue(), tailArray.get(2).floatValue());
if (fixUpAxis) {
float z = tail.z;
tail.z = -tail.y;
tail.y = z;
}
armatureWorldMatrix.mult(tail, tail);// move the tail point to global space
}
/**
* The method verifies if the given point is inside the envelope.
* @param point
* the point in 3D space (MUST be in a world coordinate space)
* @return <b>true</b> if the point is inside the envelope and <b>false</b> otherwise
*/
public boolean isInEnvelope(Vector3f point) {
Vector3f v = tail.subtract(head);
float boneLength = v.length();
v.normalizeLocal();
// computing a plane that contains 'point' and v is its normal vector
// the plane's equation is: Ax + By + Cz + D = 0, where v = [A, B, C]
float D = -v.dot(point);
// computing a point where a line that contains head and tail crosses the plane
float temp = -(v.dot(head) + D) / v.dot(v);
Vector3f p = head.add(v.x * temp, v.y * temp, v.z * temp);
// determining if the point p is on the same or other side of head than the tail point
Vector3f headToPointOnLineVector = p.subtract(head);
float headToPointLength = headToPointOnLineVector.length();
float cosinus = headToPointOnLineVector.dot(v) / headToPointLength;// the length of v is already = 1; cosinus should be either 1, 0 or -1
if (cosinus < 0 && headToPointLength > boneHeadRadius || headToPointLength > boneLength + boneTailRadius) {
return false;// the point is outside the anvelope
}
// now check if the point is inside and envelope
float pointDistanceFromLine = point.subtract(p).length(), maximumDistance = 0;
if (cosinus < 0) {
// checking if the distance from p to point is inside the half sphere defined by head envelope
// compute the distance from the line to the half sphere border
maximumDistance = boneHeadRadius;
} else if (headToPointLength < boneLength) {
// compute the maximum available distance
if (boneTailRadius > boneHeadRadius) {
// compute the distance from head to p
float headToPDistance = p.subtract(head).length();
// from tangens function we have
float x = headToPDistance * ((boneTailRadius - boneHeadRadius) / boneLength);
maximumDistance = x + boneHeadRadius;
} else if (boneTailRadius < boneHeadRadius) {
// compute the distance from head to p
float tailToPDistance = p.subtract(tail).length();
// from tangens function we have
float x = tailToPDistance * ((boneHeadRadius - boneTailRadius) / boneLength);
maximumDistance = x + boneTailRadius;
} else {
maximumDistance = boneTailRadius;
}
} else {
// checking if the distance from p to point is inside the half sphere defined by tail envelope
maximumDistance = boneTailRadius;
}
return pointDistanceFromLine <= maximumDistance + distance;
}
/**
* @return the weight of the bone
*/
public float getWeight() {
return weight;
}
@Override
public String toString() {
return "BoneEnvelope [d=" + distance + ", w=" + weight + ", hr=" + boneHeadRadius + ", tr=" + boneTailRadius + ", (" + head + ") -> (" + tail + ")]";
}
}

@ -1,317 +0,0 @@
package com.jme3.scene.plugins.blender.animations;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.curves.BezierCurve;
/**
* This class is used to calculate bezier curves value for the given frames. The
* Ipo (interpolation object) consists of several b-spline curves (connected 3rd
* degree bezier curves) of a different type.
*
* @author Marcin Roguski
*/
public class Ipo {
private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName());
public static final int AC_LOC_X = 1;
public static final int AC_LOC_Y = 2;
public static final int AC_LOC_Z = 3;
public static final int OB_ROT_X = 7;
public static final int OB_ROT_Y = 8;
public static final int OB_ROT_Z = 9;
public static final int AC_SIZE_X = 13;
public static final int AC_SIZE_Y = 14;
public static final int AC_SIZE_Z = 15;
public static final int AC_QUAT_W = 25;
public static final int AC_QUAT_X = 26;
public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28;
/** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves;
/** Each ipo contains one bone track. */
private Track calculatedTrack;
/** This variable indicates if the Y asxis is the UP axis or not. */
protected boolean fixUpAxis;
/**
* Depending on the blender version rotations are stored in degrees or
* radians so we need to know the version that is used.
*/
protected final int blenderVersion;
/**
* Constructor. Stores the bezier curves.
*
* @param bezierCurves
* a table of bezier curves
* @param fixUpAxis
* indicates if the Y is the up axis or not
* @param blenderVersion
* the blender version that is currently used
*/
public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
this.bezierCurves = bezierCurves;
this.fixUpAxis = fixUpAxis;
this.blenderVersion = blenderVersion;
}
/**
* This method calculates the ipo value for the first curve.
*
* @param frame
* the frame for which the value is calculated
* @return calculated ipo value
*/
public double calculateValue(int frame) {
return this.calculateValue(frame, 0);
}
/**
* This method calculates the ipo value for the curve of the specified
* index. Make sure you do not exceed the curves amount. Alway chech the
* amount of curves before calling this method.
*
* @param frame
* the frame for which the value is calculated
* @param curveIndex
* the index of the curve
* @return calculated ipo value
*/
public double calculateValue(int frame, int curveIndex) {
return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
}
/**
* This method returns the frame where last bezier triple center point of
* the specified bezier curve is located.
*
* @return the frame number of the last defined bezier triple point for the
* specified ipo
*/
public int getLastFrame() {
int result = 1;
for (int i = 0; i < bezierCurves.length; ++i) {
int tempResult = bezierCurves[i].getLastFrame();
if (tempResult > result) {
result = tempResult;
}
}
return result;
}
/**
* This method calculates the value of the curves as a bone track between
* the specified frames.
*
* @param targetIndex
* the index of the target for which the method calculates the
* tracks IMPORTANT! Aet to -1 (or any negative number) if you
* want to load spatial animation.
* @param localTranslation
* the local translation of the object/bone that will be animated by
* the track
* @param localRotation
* the local rotation of the object/bone that will be animated by
* the track
* @param localScale
* the local scale of the object/bone that will be animated by
* the track
* @param startFrame
* the first frame of tracks (inclusive)
* @param stopFrame
* the last frame of the tracks (inclusive)
* @param fps
* frame rate (frames per second)
* @param spatialTrack
* this flag indicates if the track belongs to a spatial or to a
* bone; the difference is important because it appears that bones
* in blender have the same type of coordinate system (Y as UP)
* as jme while other features have different one (Z is UP)
* @return bone track for the specified bone
*/
public Track calculateTrack(int targetIndex, BoneContext boneContext, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
if (calculatedTrack == null) {
// preparing data for track
int framesAmount = stopFrame - startFrame;
float timeBetweenFrames = 1.0f / fps;
float[] times = new float[framesAmount + 1];
Vector3f[] translations = new Vector3f[framesAmount + 1];
float[] translation = new float[3];
Quaternion[] rotations = new Quaternion[framesAmount + 1];
float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), };
float[] eulerRotation = localRotation.toAngles(null);
Vector3f[] scales = new Vector3f[framesAmount + 1];
float[] scale = new float[] { localScale.x, localScale.y, localScale.z };
float degreeToRadiansFactor = 1;
if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees
degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
}
int yIndex = 1, zIndex = 2;
boolean swapAxes = spatialTrack && fixUpAxis;
if (swapAxes) {
yIndex = 2;
zIndex = 1;
}
boolean eulerRotationUsed = false, queternionRotationUsed = false;
// calculating track data
for (int frame = startFrame; frame <= stopFrame; ++frame) {
boolean translationSet = false;
translation[0] = translation[1] = translation[2] = 0;
int index = frame - startFrame;
times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
for (int j = 0; j < bezierCurves.length; ++j) {
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
switch (bezierCurves[j].getType()) {
// LOCATION
case AC_LOC_X:
translation[0] = (float) value;
translationSet = true;
break;
case AC_LOC_Y:
if (swapAxes && value != 0) {
value = -value;
}
translation[yIndex] = (float) value;
translationSet = true;
break;
case AC_LOC_Z:
translation[zIndex] = (float) value;
translationSet = true;
break;
// EULER ROTATION
case OB_ROT_X:
eulerRotationUsed = true;
eulerRotation[0] = (float) value * degreeToRadiansFactor;
break;
case OB_ROT_Y:
eulerRotationUsed = true;
if (swapAxes && value != 0) {
value = -value;
}
eulerRotation[yIndex] = (float) value * degreeToRadiansFactor;
break;
case OB_ROT_Z:
eulerRotationUsed = true;
eulerRotation[zIndex] = (float) value * degreeToRadiansFactor;
break;
// SIZE
case AC_SIZE_X:
scale[0] = (float) value;
break;
case AC_SIZE_Y:
scale[yIndex] = (float) value;
break;
case AC_SIZE_Z:
scale[zIndex] = (float) value;
break;
// QUATERNION ROTATION (used with bone animation)
case AC_QUAT_W:
queternionRotationUsed = true;
quaternionRotation[3] = (float) value;
break;
case AC_QUAT_X:
queternionRotationUsed = true;
quaternionRotation[0] = (float) value;
break;
case AC_QUAT_Y:
queternionRotationUsed = true;
if (swapAxes && value != 0) {
value = -value;
}
quaternionRotation[yIndex] = (float) value;
break;
case AC_QUAT_Z:
quaternionRotation[zIndex] = (float) value;
break;
default:
LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType());
}
}
if(translationSet) {
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
} else {
translations[index] = new Vector3f();
}
if(boneContext != null) {
if(boneContext.getBone().getParent() == null && boneContext.is(BoneContext.NO_LOCAL_LOCATION)) {
float temp = translations[index].z;
translations[index].z = -translations[index].y;
translations[index].y = temp;
}
}
if (queternionRotationUsed) {
rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
} else {
rotations[index] = new Quaternion().fromAngles(eulerRotation);
}
scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
}
if (spatialTrack) {
calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
} else {
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
}
if (queternionRotationUsed && eulerRotationUsed) {
LOGGER.warning("Animation uses both euler and quaternion tracks for rotations. Quaternion rotation is applied. Make sure that this is what you wanted!");
}
}
return calculatedTrack;
}
/**
* Ipo constant curve. This is a curve with only one value and no specified
* type. This type of ipo cannot be used to calculate tracks. It should only
* be used to calculate single value for a given frame.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */static class ConstIpo extends Ipo {
/** The constant value of this ipo. */
private float constValue;
/**
* Constructor. Stores the constant value of this ipo.
*
* @param constValue
* the constant value of this ipo
*/
public ConstIpo(float constValue) {
super(null, false, 0);// the version is not important here
this.constValue = constValue;
}
@Override
public double calculateValue(int frame) {
return constValue;
}
@Override
public double calculateValue(int frame, int curveIndex) {
return constValue;
}
@Override
public BoneTrack calculateTrack(int boneIndex, BoneContext boneContext, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) {
throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
}
}
}

@ -1,148 +0,0 @@
package com.jme3.scene.plugins.blender.cameras;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.math.FastMath;
import com.jme3.renderer.Camera;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* A class that is used to load cameras into the scene.
* @author Marcin Roguski
*/
public class CameraHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(CameraHelper.class.getName());
protected static final int DEFAULT_CAM_WIDTH = 640;
protected static final int DEFAULT_CAM_HEIGHT = 480;
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
* different blender versions.
* @param blenderVersion
* the version read from the blend file
* @param blenderContext
* the blender context
*/
public CameraHelper(String blenderVersion, BlenderContext blenderContext) {
super(blenderVersion, blenderContext);
}
/**
* This method converts the given structure to jme camera.
*
* @param structure
* camera structure
* @return jme camera object
* @throws BlenderFileException
* an exception is thrown when there are problems with the
* blender file
*/
public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion >= 250) {
return this.toCamera250(structure, blenderContext.getSceneStructure());
} else {
return this.toCamera249(structure);
}
}
/**
* This method converts the given structure to jme camera. Should be used form blender 2.5+.
*
* @param structure
* camera structure
* @param sceneStructure
* scene structure
* @return jme camera object
* @throws BlenderFileException
* an exception is thrown when there are problems with the
* blender file
*/
private Camera toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException {
int width = DEFAULT_CAM_WIDTH;
int height = DEFAULT_CAM_HEIGHT;
if (sceneStructure != null) {
Structure renderData = (Structure) sceneStructure.getFieldValue("r");
width = ((Number) renderData.getFieldValue("xsch")).shortValue();
height = ((Number) renderData.getFieldValue("ysch")).shortValue();
}
Camera camera = new Camera(width, height);
int type = ((Number) structure.getFieldValue("type")).intValue();
if (type != 0 && type != 1) {
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
type = 0;
}
// type==0 - perspective; type==1 - orthographic; perspective is used as default
camera.setParallelProjection(type == 1);
float aspect = width / (float) height;
float fovY; // Vertical field of view in degrees
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
if (type == 0) {
// Convert lens MM to vertical degrees in fovY, see Blender rna_Camera_angle_get()
// Default sensor size prior to 2.60 was 32.
float sensor = 32.0f;
boolean sensorVertical = false;
Number sensorFit = (Number) structure.getFieldValue("sensor_fit");
if (sensorFit != null) {
// If sensor_fit is vert (2), then sensor_y is used
sensorVertical = sensorFit.byteValue() == 2;
String sensorName = "sensor_x";
if (sensorVertical) {
sensorName = "sensor_y";
}
sensor = ((Number) structure.getFieldValue(sensorName)).floatValue();
}
float focalLength = ((Number) structure.getFieldValue("lens")).floatValue();
float fov = 2.0f * FastMath.atan(sensor / 2.0f / focalLength);
if (sensorVertical) {
fovY = fov * FastMath.RAD_TO_DEG;
} else {
// Convert fov from horizontal to vertical
fovY = 2.0f * FastMath.atan(FastMath.tan(fov / 2.0f) / aspect) * FastMath.RAD_TO_DEG;
}
} else {
// This probably is not correct.
fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
}
camera.setFrustumPerspective(fovY, aspect, clipsta, clipend);
camera.setName(structure.getName());
return camera;
}
/**
* This method converts the given structure to jme camera. Should be used form blender 2.49.
*
* @param structure
* camera structure
* @return jme camera object
* @throws BlenderFileException
* an exception is thrown when there are problems with the
* blender file
*/
private Camera toCamera249(Structure structure) throws BlenderFileException {
Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
int type = ((Number) structure.getFieldValue("type")).intValue();
if (type != 0 && type != 1) {
LOGGER.log(Level.WARNING, "Unknown camera type: {0}. Perspective camera is being used!", type);
type = 0;
}
// type==0 - perspective; type==1 - orthographic; perspective is used as default
camera.setParallelProjection(type == 1);
float aspect = 0;
float clipsta = ((Number) structure.getFieldValue("clipsta")).floatValue();
float clipend = ((Number) structure.getFieldValue("clipend")).floatValue();
if (type == 0) {
aspect = ((Number) structure.getFieldValue("lens")).floatValue();
} else {
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
}
camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend);
camera.setName(structure.getName());
return camera;
}
}

@ -1,88 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/**
* Constraint applied on the bone.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class BoneConstraint extends Constraint {
private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
/**
* The bone constraint constructor.
*
* @param constraintStructure
* the constraint's structure
* @param ownerOMA
* the OMA of the bone that owns the constraint
* @param influenceIpo
* the influence interpolation curve
* @param blenderContext
* the blender context
* @throws BlenderFileException
* exception thrown when problems with blender file occur
*/
public BoneConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public boolean validate() {
if (targetOMA != null) {
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE);
if (nodeTarget == null) {
LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name);
return false;
}
// the second part of the if expression verifies if the found node
// (if any) is an armature node
if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
if (subtargetName.trim().isEmpty()) {
LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
return false;
}
// if the target is not an object node then it is an Armature,
// so make sure the bone is in the current skeleton
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
return false;
}
}
}
return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
}
@Override
public void apply(int frame) {
super.apply(frame);
blenderContext.getBoneContext(ownerOMA).getBone().updateModelTransforms();
}
@Override
public Long getTargetOMA() {
if(targetOMA != null && subtargetName != null && !subtargetName.trim().isEmpty()) {
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE);
if(nodeTarget != null) {
if(blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
BoneContext boneContext = blenderContext.getBoneByName(targetOMA, subtargetName);
return boneContext != null ? boneContext.getBoneOma() : 0L;
}
return targetOMA;
}
}
return 0L;
}
}

@ -1,188 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinition;
import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinitionFactory;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* The implementation of a constraint.
*
* @author Marcin Roguski (Kaelthas)
*/
public abstract class Constraint {
private static final Logger LOGGER = Logger.getLogger(Constraint.class.getName());
/** The name of this constraint. */
protected final String name;
/** Indicates if the constraint is already baked or not. */
protected boolean baked;
protected Space ownerSpace;
protected final ConstraintDefinition constraintDefinition;
protected Long ownerOMA;
protected Long targetOMA;
protected Space targetSpace;
protected String subtargetName;
/** The ipo object defining influence. */
protected final Ipo ipo;
/** The blender context. */
protected final BlenderContext blenderContext;
protected final ConstraintHelper constraintHelper;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param ownerOMA
* the old memory address of the constraint owner
* @param ownerType
* the type of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
this.blenderContext = blenderContext;
name = constraintStructure.getFieldValue("name").toString();
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
if (pData.isNotNull()) {
Structure data = pData.fetchData().get(0);
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, name, ownerOMA, blenderContext);
Pointer pTar = (Pointer) data.getFieldValue("tar");
if (pTar != null && pTar.isNotNull()) {
targetOMA = pTar.getOldMemoryAddress();
targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
Object subtargetValue = data.getFieldValue("subtarget");
if (subtargetValue != null) {// not all constraint data have the
// subtarget field
subtargetName = subtargetValue.toString();
}
}
} else {
// Null constraint has no data, so create it here
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, name, null, blenderContext);
}
ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
ipo = influenceIpo;
this.ownerOMA = ownerOMA;
constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
}
/**
* @return <b>true</b> if the constraint is implemented and <b>false</b>
* otherwise
*/
public boolean isImplemented() {
return constraintDefinition == null ? true : constraintDefinition.isImplemented();
}
/**
* @return the name of the constraint type, similar to the constraint name
* used in Blender
*/
public String getConstraintTypeName() {
return constraintDefinition.getConstraintTypeName();
}
/**
* @return the OMAs of the features whose transform had been altered beside the constraint owner
*/
public Set<Long> getAlteredOmas() {
return constraintDefinition.getAlteredOmas();
}
/**
* Performs validation before baking. Checks factors that can prevent
* constraint from baking that could not be checked during constraint
* loading.
*/
public abstract boolean validate();
/**
* @return the OMA of the target or 0 if no target is specified for the constraint
*/
public abstract Long getTargetOMA();
/**
* Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
* @param frame
* the frame of the animation
*/
public void apply(int frame) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Applying constraint: {0} for frame {1}", new Object[] { name, frame });
}
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float) ipo.calculateValue(frame));
}
/**
* @return determines if the definition of the constraint will change the bone in any way; in most cases
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
* computing to improve the computation speed and lower the computations complexity
*/
public boolean isTrackToBeChanged() {
return constraintDefinition == null ? false : constraintDefinition.isTrackToBeChanged();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
result = prime * result + (ownerOMA == null ? 0 : ownerOMA.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
Constraint other = (Constraint) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (ownerOMA == null) {
if (other.ownerOMA != null) {
return false;
}
} else if (!ownerOMA.equals(other.ownerOMA)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Constraint(name = " + name + ", def = " + constraintDefinition + ")";
}
}

@ -1,476 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.AnimationHelper;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.util.TempVars;
/**
* This class should be used for constraint calculations.
*
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
/**
* Helper constructor.
*
* @param blenderVersion
* the version read from the blend file
* @param blenderContext
* the blender context
*/
public ConstraintHelper(String blenderVersion, BlenderContext blenderContext) {
super(blenderVersion, blenderContext);
}
/**
* This method reads constraints for for the given structure. The
* constraints are loaded only once for object/bone.
*
* @param objectStructure
* the structure we read constraint's for
* @param blenderContext
* the blender context
* @throws BlenderFileException
*/
public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.fine("Loading constraints.");
// reading influence ipos for the constraints
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
if (pActions.isNotNull()) {
List<Structure> actions = pActions.fetchData();
for (Structure action : actions) {
Structure chanbase = (Structure) action.getFieldValue("chanbase");
List<Structure> actionChannels = chanbase.evaluateListBase();
for (Structure actionChannel : actionChannels) {
Map<String, Ipo> ipos = new HashMap<String, Ipo>();
Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
List<Structure> constraintChannels = constChannels.evaluateListBase();
for (Structure constraintChannel : constraintChannels) {
Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
if (pIpo.isNotNull()) {
String constraintName = constraintChannel.getFieldValue("name").toString();
Ipo ipo = animationHelper.fromIpoStructure(pIpo.fetchData().get(0), blenderContext);
ipos.put(constraintName, ipo);
}
}
String actionName = actionChannel.getFieldValue("name").toString();
constraintsIpos.put(actionName, ipos);
}
}
}
// loading constraints connected with the object's bones
Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
if (pPose.isNotNull()) {
List<Structure> poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
for (Structure poseChannel : poseChannels) {
List<Constraint> constraintsList = new ArrayList<Constraint>();
Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
// the name is read directly from structure because bone might
// not yet be loaded
String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase();
for (Structure constraint : constraints) {
String constraintName = constraint.getFieldValue("name").toString();
Map<String, Ipo> ipoMap = constraintsIpos.get(name);
Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName);
if (ipo == null) {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = animationHelper.fromValue(enforce);
}
constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
}
blenderContext.addConstraints(boneOMA, constraintsList);
}
}
// loading constraints connected with the object itself
List<Structure> constraints = ((Structure) objectStructure.getFieldValue("constraints")).evaluateListBase();
if (constraints != null && constraints.size() > 0) {
Pointer pData = (Pointer) objectStructure.getFieldValue("data");
String dataType = pData.isNotNull() ? pData.fetchData().get(0).getType() : null;
List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
for (Structure constraint : constraints) {
String constraintName = constraint.getFieldValue("name").toString();
String objectName = objectStructure.getName();
Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null;
if (ipo == null) {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = animationHelper.fromValue(enforce);
}
constraintsList.add(this.createConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
}
blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
}
}
/**
* This method creates a proper constraint object depending on the object's
* data type. Supported data types: <li>Mesh <li>Armature <li>Camera <li>
* Lamp Bone constraints are created in a different place.
*
* @param dataType
* the type of the object's data
* @param constraintStructure
* the constraint structure
* @param ownerOMA
* the owner OMA
* @param influenceIpo
* the influence interpolation curve
* @param blenderContext
* the blender context
* @return constraint object for the required type
* @throws BlenderFileException
* thrown when problems with blender file occured
*/
private Constraint createConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
} else if ("Armature".equalsIgnoreCase(dataType)) {
return new SkeletonConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
} else {
throw new IllegalArgumentException("Unsupported data type for applying constraints: " + dataType);
}
}
/**
* The method bakes all available and valid constraints.
*
* @param blenderContext
* the blender context
*/
public void bakeConstraints(BlenderContext blenderContext) {
Set<Long> owners = new HashSet<Long>();
for (Constraint constraint : blenderContext.getAllConstraints()) {
if(constraint instanceof BoneConstraint) {
BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA);
owners.add(boneContext.getArmatureObjectOMA());
} else {
Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedDataType.FEATURE);
while (spatial.getParent() != null) {
spatial = spatial.getParent();
}
owners.add((Long)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, spatial));
}
}
List<SimulationNode> simulationRootNodes = new ArrayList<SimulationNode>(owners.size());
for(Long ownerOMA : owners) {
simulationRootNodes.add(new SimulationNode(ownerOMA, blenderContext));
}
for (SimulationNode node : simulationRootNodes) {
node.simulate();
}
}
/**
* The method retreives the transform from a feature in a given space.
*
* @param oma
* the OMA of the feature (spatial or armature node)
* @param subtargetName
* the feature's subtarget (bone in a case of armature's node)
* @param space
* the space the transform is evaluated to
* @return thensform of a feature in a given space
*/
public Transform getTransform(Long oma, String subtargetName, Space space) {
Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE);
boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null;
if (isArmature) {
blenderContext.getSkeleton(oma).updateWorldVectors();
BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName);
Bone bone = targetBoneContext.getBone();
if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) {
space = Space.CONSTRAINT_SPACE_POSE;
}
TempVars tempVars = TempVars.get();// use readable names of the matrices so that the code is more clear
Transform result;
switch (space) {
case CONSTRAINT_SPACE_WORLD:
Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedDataType.FEATURE);
Matrix4f boneModelMatrix = this.toMatrix(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale(), tempVars.tempMat4);
Matrix4f modelWorldMatrix = this.toMatrix(model.getWorldTransform(), tempVars.tempMat42);
Matrix4f boneMatrixInWorldSpace = modelWorldMatrix.multLocal(boneModelMatrix);
result = new Transform(boneMatrixInWorldSpace.toTranslationVector(), boneMatrixInWorldSpace.toRotationQuat(), boneMatrixInWorldSpace.toScaleVector());
break;
case CONSTRAINT_SPACE_LOCAL:
assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!";
result = new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
break;
case CONSTRAINT_SPACE_POSE: {
Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4);
Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal();
Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix);
result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector());
break;
}
case CONSTRAINT_SPACE_PARLOCAL: {
Matrix4f boneWorldMatrix = this.toMatrix(this.getTransform(oma, subtargetName, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat4);
Matrix4f armatureInvertedWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat42).invertLocal();
Matrix4f bonePoseMatrix = armatureInvertedWorldMatrix.multLocal(boneWorldMatrix);
result = new Transform(bonePoseMatrix.toTranslationVector(), bonePoseMatrix.toRotationQuat(), bonePoseMatrix.toScaleVector());
Bone parent = bone.getParent();
if(parent != null) {
BoneContext parentContext = blenderContext.getBoneContext(parent);
Vector3f head = parent.getModelSpacePosition();
Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(parentContext.getLength())));
result.getTranslation().subtractLocal(tail);
}
break;
}
default:
throw new IllegalStateException("Unknown space type: " + space);
}
tempVars.release();
return result;
} else {
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
return feature.getLocalTransform();
case CONSTRAINT_SPACE_WORLD:
return feature.getWorldTransform();
case CONSTRAINT_SPACE_PARLOCAL:
case CONSTRAINT_SPACE_POSE:
throw new IllegalStateException("Nodes can have only Local and World spaces applied!");
default:
throw new IllegalStateException("Unknown space type: " + space);
}
}
}
/**
* Applies transform to a feature (bone or spatial). Computations transform
* the given transformation from the given space to the feature's local
* space.
*
* @param oma
* the OMA of the feature we apply transformation to
* @param subtargetName
* the name of the feature's subtarget (bone in case of armature)
* @param space
* the space in which the given transform is to be applied
* @param transform
* the transform we apply
*/
public void applyTransform(Long oma, String subtargetName, Space space, Transform transform) {
Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedDataType.FEATURE);
boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null;
if (isArmature) {
Skeleton skeleton = blenderContext.getSkeleton(oma);
BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName);
Bone bone = targetBoneContext.getBone();
if (bone.getParent() == null && (space == Space.CONSTRAINT_SPACE_LOCAL || space == Space.CONSTRAINT_SPACE_PARLOCAL)) {
space = Space.CONSTRAINT_SPACE_POSE;
}
TempVars tempVars = TempVars.get();
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
assert bone.getParent() != null : "CONSTRAINT_SPACE_LOCAL should be evaluated as CONSTRAINT_SPACE_POSE if the bone has no parent!";
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
break;
case CONSTRAINT_SPACE_WORLD: {
Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform, tempVars.tempMat4);
Matrix4f modelWorldMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42);
Matrix4f boneMatrixInModelSpace = modelWorldMatrix.invertLocal().multLocal(boneMatrixInWorldSpace);
Bone parent = bone.getParent();
if (parent != null) {
Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4);
boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace);
}
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
break;
}
case CONSTRAINT_SPACE_POSE: {
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4);
Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42));
Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal();
Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace);
Bone parent = bone.getParent();
if (parent != null) {
Matrix4f parentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4);
boneMatrixInModelSpace = parentMatrixInModelSpace.invertLocal().multLocal(boneMatrixInModelSpace);
}
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
break;
}
case CONSTRAINT_SPACE_PARLOCAL:
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform(), tempVars.tempMat4);
Matrix4f boneMatrixInWorldSpace = armatureWorldMatrix.multLocal(this.toMatrix(transform, tempVars.tempMat42));
Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD), tempVars.tempMat42).invertLocal();
Matrix4f boneMatrixInModelSpace = invertedModelMatrix.multLocal(boneMatrixInWorldSpace);
Bone parent = bone.getParent();
if (parent != null) {
//first add the initial parent matrix to the bone's model matrix
BoneContext parentContext = blenderContext.getBoneContext(parent);
Matrix4f initialParentMatrixInModelSpace = parentContext.getBoneMatrixInModelSpace();
Matrix4f currentParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale(), tempVars.tempMat4);
//the bone will now move with its parent in model space
//now we need to subtract the difference between current parent's model matrix and its initial model matrix
boneMatrixInModelSpace = initialParentMatrixInModelSpace.mult(boneMatrixInModelSpace);
Matrix4f diffMatrix = initialParentMatrixInModelSpace.mult(currentParentMatrixInModelSpace.invert());
boneMatrixInModelSpace.multLocal(diffMatrix);
//now the bone will have its position in model space with initial parent's model matrix added
}
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
break;
default:
tempVars.release();
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
}
tempVars.release();
skeleton.updateWorldVectors();
} else {
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
feature.getLocalTransform().set(transform);
break;
case CONSTRAINT_SPACE_WORLD:
if (feature.getParent() == null) {
feature.setLocalTransform(transform);
} else {
Transform parentWorldTransform = feature.getParent().getWorldTransform();
TempVars tempVars = TempVars.get();
Matrix4f parentInverseMatrix = this.toMatrix(parentWorldTransform, tempVars.tempMat4).invertLocal();
Matrix4f m = this.toMatrix(transform, tempVars.tempMat42);
m = m.multLocal(parentInverseMatrix);
tempVars.release();
transform.setTranslation(m.toTranslationVector());
transform.setRotation(m.toRotationQuat());
transform.setScale(m.toScaleVector());
feature.setLocalTransform(transform);
}
break;
default:
throw new IllegalStateException("Invalid space type for spatial object: " + space.toString());
}
}
}
/**
* Converts given transform to the matrix.
*
* @param transform
* the transform to be converted
* @param store
* the matrix where the result will be stored
* @return the store matrix
*/
public Matrix4f toMatrix(Transform transform, Matrix4f store) {
if (transform != null) {
return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale(), store);
}
store.loadIdentity();
return store;
}
/**
* Converts given transformation parameters into the matrix.
*
* @param position
* the position of the feature
* @param rotation
* the rotation of the feature
* @param scale
* the scale of the feature
* @param store
* the matrix where the result will be stored
* @return the store matrix
*/
private Matrix4f toMatrix(Vector3f position, Quaternion rotation, Vector3f scale, Matrix4f store) {
store.loadIdentity();
store.setTranslation(position);
store.setRotationQuaternion(rotation);
store.setScale(scale);
return store;
}
/**
* The space of target or owner transformation.
*
* @author Marcin Roguski (Kaelthas)
*/
public static enum Space {
/** A transformation of the bone or spatial in the world space. */
CONSTRAINT_SPACE_WORLD,
/**
* For spatial it is the transformation in its parent space or in WORLD space if it has no parent.
* For bone it is a transformation in its bone parent space or in armature space if it has no parent.
*/
CONSTRAINT_SPACE_LOCAL,
/**
* This space IS NOT applicable for spatials.
* For bone it is a transformation in the blender's armature object space.
*/
CONSTRAINT_SPACE_POSE,
CONSTRAINT_SPACE_PARLOCAL;
/**
* This method returns the enum instance when given the appropriate
* value from the blend file.
*
* @param c
* the blender's value of the space modifier
* @return the scape enum instance
*/
public static Space valueOf(byte c) {
switch (c) {
case 0:
return CONSTRAINT_SPACE_WORLD;
case 1:
return CONSTRAINT_SPACE_LOCAL;
case 2:
return CONSTRAINT_SPACE_POSE;
case 3:
return CONSTRAINT_SPACE_PARLOCAL;
default:
throw new IllegalArgumentException("Value: " + c + " cannot be converted to Space enum instance!");
}
}
}
}

@ -1,397 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.util.TempVars;
/**
* A node that represents either spatial or bone in constraint simulation. The
* node is applied its translation, rotation and scale for each frame of its
* animation. Then the constraints are applied that will eventually alter it.
* After that the feature's transformation is stored in VirtualTrack which is
* converted to new bone or spatial track at the very end.
*
* @author Marcin Roguski (Kaelthas)
*/
public class SimulationNode {
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
private Long featureOMA;
/** The blender context. */
private BlenderContext blenderContext;
/** The name of the node (for debugging purposes). */
private String name;
/** A list of children for the node (either bones or child spatials). */
private List<SimulationNode> children = new ArrayList<SimulationNode>();
/** A list of node's animations. */
private List<Animation> animations;
/** The nodes spatial (if null then the boneContext should be set). */
private Spatial spatial;
/** The skeleton of the bone (not null if the node simulated the bone). */
private Skeleton skeleton;
/** Animation controller for the node's feature. */
private AnimControl animControl;
/**
* The star transform of a spatial. Needed to properly reset the spatial to
* its start position.
*/
private Transform spatialStartTransform;
/** Star transformations for bones. Needed to properly reset the bones. */
private Map<Bone, Transform> boneStartTransforms;
/**
* Builds the nodes tree for the given feature. The feature (bone or
* spatial) is found by its OMA. The feature must be a root bone or a root
* spatial.
*
* @param featureOMA
* the OMA of either bone or spatial
* @param blenderContext
* the blender context
*/
public SimulationNode(Long featureOMA, BlenderContext blenderContext) {
this(featureOMA, blenderContext, true);
}
/**
* Creates the node for the feature.
*
* @param featureOMA
* the OMA of either bone or spatial
* @param blenderContext
* the blender context
* @param rootNode
* indicates if the feature is a root bone or root spatial or not
*/
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
this.featureOMA = featureOMA;
this.blenderContext = blenderContext;
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedDataType.FEATURE);
if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) {
skeleton = blenderContext.getSkeleton(featureOMA);
Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
animControl = nodeWithAnimationControl.getControl(AnimControl.class);
boneStartTransforms = new HashMap<Bone, Transform>();
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
Bone bone = skeleton.getBone(i);
boneStartTransforms.put(bone, new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale()));
}
} else {
if (rootNode && spatial.getParent() != null) {
throw new IllegalStateException("Given spatial must be a root node!");
}
this.spatial = spatial;
spatialStartTransform = spatial.getLocalTransform().clone();
}
name = '>' + spatial.getName() + '<';
// add children nodes
if (skeleton != null) {
Node node = blenderContext.getControlledNode(skeleton);
Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
animations = blenderContext.getAnimations(animatedNodeOMA);
} else {
animations = blenderContext.getAnimations(featureOMA);
for (Spatial child : spatial.getChildren()) {
if (child instanceof Node) {
children.add(new SimulationNode((Long) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, child), blenderContext, false));
}
}
}
}
/**
* Resets the node's feature to its starting transformation.
*/
private void reset() {
if (spatial != null) {
spatial.setLocalTransform(spatialStartTransform);
for (SimulationNode child : children) {
child.reset();
}
} else if (skeleton != null) {
for (Entry<Bone, Transform> entry : boneStartTransforms.entrySet()) {
Transform t = entry.getValue();
entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
entry.getKey().updateModelTransforms();
}
skeleton.reset();
}
}
/**
* Simulates the spatial node.
*/
private void simulateSpatial() {
List<Constraint> constraints = blenderContext.getConstraints(featureOMA);
if (constraints != null && constraints.size() > 0) {
LOGGER.fine("Simulating spatial.");
boolean applyStaticConstraints = true;
if (animations != null) {
for (Animation animation : animations) {
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
int maxFrame = (int) animationTimeBoundaries[0];
float maxTime = animationTimeBoundaries[1];
VirtualTrack vTrack = new VirtualTrack(spatial.getName(), maxFrame, maxTime);
for (Track track : animation.getTracks()) {
for (int frame = 0; frame < maxFrame; ++frame) {
spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]);
spatial.setLocalRotation(((SpatialTrack) track).getRotations()[frame]);
spatial.setLocalScale(((SpatialTrack) track).getScales()[frame]);
for (Constraint constraint : constraints) {
constraint.apply(frame);
vTrack.setTransform(frame, spatial.getLocalTransform());
}
}
Track newTrack = vTrack.getAsSpatialTrack();
if (newTrack != null) {
animation.removeTrack(track);
animation.addTrack(newTrack);
}
applyStaticConstraints = false;
}
}
}
// if there are no animations then just constraint the static
// object's transformation
if (applyStaticConstraints) {
for (Constraint constraint : constraints) {
constraint.apply(0);
}
}
}
for (SimulationNode child : children) {
child.simulate();
}
}
/**
* Simulates the bone node.
*/
private void simulateSkeleton() {
LOGGER.fine("Simulating skeleton.");
Set<Long> alteredOmas = new HashSet<Long>();
if (animations != null) {
TempVars vars = TempVars.get();
AnimChannel animChannel = animControl.createChannel();
for (Animation animation : animations) {
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
int maxFrame = (int) animationTimeBoundaries[0];
float maxTime = animationTimeBoundaries[1];
Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
for (int frame = 0; frame < maxFrame; ++frame) {
// this MUST be done here, otherwise setting next frame of animation will
// lead to possible errors
this.reset();
// first set proper time for all bones in all the tracks ...
for (Track track : animation.getTracks()) {
float time = ((BoneTrack) track).getTimes()[frame];
track.setTime(time, 1, animControl, animChannel, vars);
skeleton.updateWorldVectors();
}
// ... and then apply constraints from the root bone to the last child ...
Set<Long> applied = new HashSet<Long>();
for (Bone rootBone : skeleton.getRoots()) {
// ignore the 0-indexed bone
if (skeleton.getBoneIndex(rootBone) > 0) {
this.applyConstraints(rootBone, alteredOmas, applied, frame, new Stack<Bone>());
}
}
// ... add virtual tracks if necessary, for bones that were altered but had no tracks before ...
for (Long boneOMA : alteredOmas) {
BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
if (!tracks.containsKey(boneIndex)) {
tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
}
}
alteredOmas.clear();
// ... and fill in another frame in the result track
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
Bone bone = skeleton.getBone(trackEntry.getKey());
Transform startTransform = boneStartTransforms.get(bone);
// track contains differences between the frame position and bind positions of bones/spatials
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
}
}
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
if (newTrack != null) {
boolean trackReplaced = false;
for (Track track : animation.getTracks()) {
if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
animation.removeTrack(track);
animation.addTrack(newTrack);
trackReplaced = true;
break;
}
}
if (!trackReplaced) {
animation.addTrack(newTrack);
}
}
}
}
vars.release();
animControl.clearChannels();
this.reset();
}
}
/**
* Applies constraints to the given bone and its children.
* The goal is to apply constraint from root bone to the last child.
* @param bone
* the bone whose constraints will be applied
* @param alteredOmas
* the set of OMAS of the altered bones (is populated if necessary)
* @param frame
* the current frame of the animation
* @param bonesStack
* the stack of bones used to avoid infinite loops while applying constraints
*/
private void applyConstraints(Bone bone, Set<Long> alteredOmas, Set<Long> applied, int frame, Stack<Bone> bonesStack) {
if (!bonesStack.contains(bone)) {
bonesStack.push(bone);
BoneContext boneContext = blenderContext.getBoneContext(bone);
if (!applied.contains(boneContext.getBoneOma())) {
List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
if (constraints != null && constraints.size() > 0) {
for (Constraint constraint : constraints) {
if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) {
// first apply constraints of the target bone
BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA());
this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame, bonesStack);
}
constraint.apply(frame);
if (constraint.getAlteredOmas() != null) {
alteredOmas.addAll(constraint.getAlteredOmas());
}
alteredOmas.add(boneContext.getBoneOma());
}
}
applied.add(boneContext.getBoneOma());
}
List<Bone> children = bone.getChildren();
if (children != null && children.size() > 0) {
for (Bone child : bone.getChildren()) {
this.applyConstraints(child, alteredOmas, applied, frame, bonesStack);
}
}
bonesStack.pop();
}
}
/**
* Simulates the node.
*/
public void simulate() {
this.reset();
if (spatial != null) {
this.simulateSpatial();
} else {
this.simulateSkeleton();
}
}
/**
* Computes the maximum frame and time for the animation. Different tracks
* can have different lengths so here the maximum one is being found.
*
* @param animation
* the animation
* @return maximum frame and time of the animation
*/
private float[] computeAnimationTimeBoundaries(Animation animation) {
int maxFrame = Integer.MIN_VALUE;
float maxTime = -Float.MAX_VALUE;
for (Track track : animation.getTracks()) {
if (track instanceof BoneTrack) {
maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length);
maxTime = Math.max(maxTime, ((BoneTrack) track).getTimes()[((BoneTrack) track).getTimes().length - 1]);
} else if (track instanceof SpatialTrack) {
maxFrame = Math.max(maxFrame, ((SpatialTrack) track).getTranslations().length);
maxTime = Math.max(maxTime, ((SpatialTrack) track).getTimes()[((SpatialTrack) track).getTimes().length - 1]);
} else {
throw new IllegalStateException("Unsupported track type for simuation: " + track);
}
}
return new float[] { maxFrame, maxTime };
}
/**
* Finds constraints for the node's features.
*
* @param ownerOMA
* the feature's OMA
* @param blenderContext
* the blender context
* @return a list of feature's constraints or empty list if none were found
*/
private List<Constraint> findConstraints(Long ownerOMA, BlenderContext blenderContext) {
List<Constraint> result = new ArrayList<Constraint>();
List<Constraint> constraints = blenderContext.getConstraints(ownerOMA);
if (constraints != null) {
for (Constraint constraint : constraints) {
if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) {
result.add(constraint);
}
// TODO: add proper warnings to some map or set so that they are not logged on every frame
}
}
return result.size() > 0 ? result : null;
}
@Override
public String toString() {
return name;
}
}

@ -1,41 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* Constraint applied on the skeleton. This constraint is here only to make the
* application not crash when loads constraints applied to armature. But
* skeleton movement is not supported by jme so the constraint will never be
* applied.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class SkeletonConstraint extends Constraint {
private static final Logger LOGGER = Logger.getLogger(SkeletonConstraint.class.getName());
public SkeletonConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public boolean validate() {
LOGGER.warning("Constraints for skeleton are not supported.");
return false;
}
@Override
public void apply(int frame) {
LOGGER.warning("Applying constraints to skeleton is not supported.");
}
@Override
public Long getTargetOMA() {
LOGGER.warning("Constraints for skeleton are not supported.");
return null;
}
}

@ -1,32 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* Constraint applied on the spatial objects. This includes: nodes, cameras
* nodes and light nodes.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class SpatialConstraint extends Constraint {
public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public boolean validate() {
if (targetOMA != null) {
return blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE) != null;
}
return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
}
@Override
public Long getTargetOMA() {
return targetOMA;
}
}

@ -1,165 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.ArrayList;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
/**
* A virtual track that stores computed frames after constraints are applied.
* Not all the frames need to be inserted. If there are lacks then the class
* will fill the gaps.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class VirtualTrack {
/** The name of the track (for debugging purposes). */
private String name;
/** The last frame for the track. */
public int maxFrame;
/** The max time for the track. */
public float maxTime;
/** Translations of the track. */
public ArrayList<Vector3f> translations;
/** Rotations of the track. */
public ArrayList<Quaternion> rotations;
/** Scales of the track. */
public ArrayList<Vector3f> scales;
/**
* Constructs the object storing the maximum frame and time.
*
* @param maxFrame
* the last frame for the track
* @param maxTime
* the max time for the track
*/
public VirtualTrack(String name, int maxFrame, float maxTime) {
this.name = name;
this.maxFrame = maxFrame;
this.maxTime = maxTime;
}
/**
* Sets the transform for the given frame.
*
* @param frameIndex
* the frame for which the transform will be set
* @param transform
* the transformation to be set
*/
public void setTransform(int frameIndex, Transform transform) {
if (translations == null) {
translations = this.createList(Vector3f.ZERO, frameIndex);
}
this.append(translations, Vector3f.ZERO, frameIndex - translations.size());
translations.add(transform.getTranslation().clone());
if (rotations == null) {
rotations = this.createList(Quaternion.IDENTITY, frameIndex);
}
this.append(rotations, Quaternion.IDENTITY, frameIndex - rotations.size());
rotations.add(transform.getRotation().clone());
if (scales == null) {
scales = this.createList(Vector3f.UNIT_XYZ, frameIndex);
}
this.append(scales, Vector3f.UNIT_XYZ, frameIndex - scales.size());
scales.add(transform.getScale().clone());
}
/**
* Returns the track as a bone track.
*
* @param targetBoneIndex
* the bone index
* @return the bone track
*/
public BoneTrack getAsBoneTrack(int targetBoneIndex) {
if (translations == null && rotations == null && scales == null) {
return null;
}
return new BoneTrack(targetBoneIndex, this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame]));
}
/**
* Returns the track as a spatial track.
*
* @return the spatial track
*/
public SpatialTrack getAsSpatialTrack() {
if (translations == null && rotations == null && scales == null) {
return null;
}
return new SpatialTrack(this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame]));
}
/**
* The method creates times for the track based on the given maximum values.
*
* @return the times for the track
*/
private float[] createTimes() {
float[] times = new float[maxFrame];
float dT = maxTime / maxFrame;
float t = 0;
for (int i = 0; i < maxFrame; ++i) {
times[i] = t;
t += dT;
}
return times;
}
/**
* Helper method that creates a list of a given size filled with given
* elements.
*
* @param element
* the element to be put into the list
* @param count
* the list size
* @return the list
*/
private <T> ArrayList<T> createList(T element, int count) {
ArrayList<T> result = new ArrayList<T>(count);
for (int i = 0; i < count; ++i) {
result.add(element);
}
return result;
}
/**
* Appends the element to the given list.
*
* @param list
* the list where the element will be appended
* @param element
* the element to be appended
* @param count
* how many times the element will be appended
*/
private <T> void append(ArrayList<T> list, T element, int count) {
for (int i = 0; i < count; ++i) {
list.add(element);
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(2048);
result.append("TRACK: ").append(name).append('\n');
if (translations != null && translations.size() > 0) {
result.append("TRANSLATIONS: ").append(translations.toString()).append('\n');
}
if (rotations != null && rotations.size() > 0) {
result.append("ROTATIONS: ").append(rotations.toString()).append('\n');
}
if (scales != null && scales.size() > 0) {
result.append("SCALES: ").append(scales.toString()).append('\n');
}
return result.toString();
}
}

@ -1,162 +0,0 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import java.util.Set;
import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* A base class for all constraint definitions.
*
* @author Marcin Roguski (Kaelthas)
*/
public abstract class ConstraintDefinition {
protected ConstraintHelper constraintHelper;
/** Constraints flag. Used to load user's options applied to the constraint. */
protected int flag;
/** The constraint's owner. Loaded during runtime. */
private Object owner;
/** The blender context. */
protected BlenderContext blenderContext;
/** The constraint's owner OMA. */
protected Long ownerOMA;
/** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
protected Set<Long> alteredOmas;
/** The variable that determines if the constraint will alter the track in any way. */
protected boolean trackToBeChanged = true;
/** The name of the constraint. */
protected String constraintName;
/**
* Loads a constraint definition based on the constraint definition
* structure.
*
* @param constraintData
* the constraint definition structure
* @param ownerOMA
* the constraint's owner OMA
* @param blenderContext
* the blender context
*/
public ConstraintDefinition(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
if (constraintData != null) {// Null constraint has no data
Number flag = (Number) constraintData.getFieldValue("flag");
if (flag != null) {
this.flag = flag.intValue();
}
}
this.blenderContext = blenderContext;
constraintHelper = (ConstraintHelper) (blenderContext == null ? null : blenderContext.getHelper(ConstraintHelper.class));
this.ownerOMA = ownerOMA;
}
public void setConstraintName(String constraintName) {
this.constraintName = constraintName;
}
/**
* @return determines if the definition of the constraint will change the bone in any way; in most cases
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
* computing to improve the computation speed and lower the computations complexity
*/
public boolean isTrackToBeChanged() {
return trackToBeChanged;
}
/**
* @return determines if this constraint definition requires a defined target or not
*/
public abstract boolean isTargetRequired();
/**
* This method is here because we have no guarantee that the owner is loaded
* when constraint is being created. So use it to get the owner when it is
* needed for computations.
*
* @return the owner of the constraint or null if none is set
*/
protected Object getOwner() {
if (ownerOMA != null && owner == null) {
owner = blenderContext.getLoadedFeature(ownerOMA, LoadedDataType.FEATURE);
if (owner == null) {
throw new IllegalStateException("Cannot load constraint's owner for constraint type: " + this.getClass().getName());
}
}
return owner;
}
/**
* The method gets the owner's transformation. The owner can be either bone or spatial.
* @param ownerSpace
* the space in which the computed transformation is given
* @return the constraint owner's transformation
*/
protected Transform getOwnerTransform(Space ownerSpace) {
if (this.getOwner() instanceof Bone) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
return constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
}
return constraintHelper.getTransform(ownerOMA, null, ownerSpace);
}
/**
* The method applies the given transformation to the owner.
* @param ownerTransform
* the transformation to apply to the owner
* @param ownerSpace
* the space that defines which owner's transformation (ie. global, local, etc. will be set)
*/
protected void applyOwnerTransform(Transform ownerTransform, Space ownerSpace) {
if (this.getOwner() instanceof Bone) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
} else {
constraintHelper.applyTransform(ownerOMA, null, ownerSpace, ownerTransform);
}
}
/**
* @return <b>true</b> if the definition is implemented and <b>false</b>
* otherwise
*/
public boolean isImplemented() {
return true;
}
/**
* @return a list of all OMAs of the features that the constraint had altered beside its owner
*/
public Set<Long> getAlteredOmas() {
return alteredOmas;
}
/**
* @return the type name of the constraint
*/
public abstract String getConstraintTypeName();
/**
* Bakes the constraint for the current feature (bone or spatial) position.
*
* @param ownerSpace
* the space where owner transform will be evaluated in
* @param targetSpace
* the space where target transform will be evaluated in
* @param targetTransform
* the target transform used by some of the constraints
* @param influence
* the influence of the constraint (from range <0; 1>)
*/
public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
@Override
public String toString() {
return this.getConstraintTypeName();
}
}

@ -1,84 +0,0 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* This class represents 'Dist limit' constraint type in blender.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class ConstraintDefinitionDistLimit extends ConstraintDefinition {
private static final int LIMITDIST_INSIDE = 0;
private static final int LIMITDIST_OUTSIDE = 1;
private static final int LIMITDIST_ONSURFACE = 2;
protected int mode;
protected float dist;
public ConstraintDefinitionDistLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext);
mode = ((Number) constraintData.getFieldValue("mode")).intValue();
dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
// distance limit does not work on bones who are connected to their parent
return;
}
if (influence == 0 || targetTransform == null) {
return;// no need to do anything
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
float currentDistance = v.length();
switch (mode) {
case LIMITDIST_INSIDE:
if (currentDistance >= dist) {
v.normalizeLocal();
v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
}
break;
case LIMITDIST_ONSURFACE:
if (currentDistance > dist) {
v.normalizeLocal();
v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
ownerTransform.getTranslation().set(v.addLocal(targetTransform.getTranslation()));
} else if (currentDistance < dist) {
v.normalizeLocal().multLocal(dist * influence);
ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
}
break;
case LIMITDIST_OUTSIDE:
if (currentDistance <= dist) {
v = targetTransform.getTranslation().subtract(ownerTransform.getTranslation()).normalizeLocal().multLocal(dist * influence);
ownerTransform.getTranslation().set(targetTransform.getTranslation().add(v));
}
break;
default:
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@Override
public boolean isTargetRequired() {
return true;
}
@Override
public String getConstraintTypeName() {
return "Limit distance";
}
}

@ -1,126 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene.plugins.blender.constraints.definitions;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
public class ConstraintDefinitionFactory {
private static final Map<String, Class<? extends ConstraintDefinition>> CONSTRAINT_CLASSES = new HashMap<String, Class<? extends ConstraintDefinition>>();
static {
CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class);
CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class);
CONSTRAINT_CLASSES.put("bLocLimitConstraint", ConstraintDefinitionLocLimit.class);
CONSTRAINT_CLASSES.put("bNullConstraint", ConstraintDefinitionNull.class);
CONSTRAINT_CLASSES.put("bRotateLikeConstraint", ConstraintDefinitionRotLike.class);
CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionIK.class);
CONSTRAINT_CLASSES.put("bTransLikeConstraint", ConstraintDefinitionTransLike.class);// since blender 2.51
CONSTRAINT_CLASSES.put("bSameVolumeConstraint", ConstraintDefinitionMaintainVolume.class);// since blender 2.53
}
private static final Map<String, String> UNSUPPORTED_CONSTRAINTS = new HashMap<String, String>();
static {
UNSUPPORTED_CONSTRAINTS.put("bActionConstraint", "Action");
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to");
UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path");
UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track");
UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max");
UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script");
UNSUPPORTED_CONSTRAINTS.put("bRigidBodyJointConstraint", "Rigid body joint");
UNSUPPORTED_CONSTRAINTS.put("bShrinkWrapConstraint", "Shrinkwrap");
UNSUPPORTED_CONSTRAINTS.put("bStretchToConstraint", "Stretch to");
UNSUPPORTED_CONSTRAINTS.put("bTransformConstraint", "Transform");
// Blender 2.50+
UNSUPPORTED_CONSTRAINTS.put("bSplineIKConstraint", "Spline inverse kinematics");
UNSUPPORTED_CONSTRAINTS.put("bDampTrackConstraint", "Damp track");
UNSUPPORTED_CONSTRAINTS.put("bPivotConstraint", "Pivot");
// Blender 2.56+
UNSUPPORTED_CONSTRAINTS.put("bTrackToConstraint", "Track to");
// Blender 2.62+
UNSUPPORTED_CONSTRAINTS.put("bCameraSolverConstraint", "Camera solver");
UNSUPPORTED_CONSTRAINTS.put("bObjectSolverConstraint", "Object solver");
UNSUPPORTED_CONSTRAINTS.put("bFollowTrackConstraint", "Follow track");
}
/**
* This method creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* If the value is null the NullConstraint is created.
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, String constraintName, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException {
if (constraintStructure == null) {
return new ConstraintDefinitionNull(null, ownerOMA, blenderContext);
}
String constraintClassName = constraintStructure.getType();
Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
if (constraintDefinitionClass != null) {
try {
ConstraintDefinition def = (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext);
def.setConstraintName(constraintName);
return def;
} catch (IllegalArgumentException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (SecurityException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (InstantiationException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (IllegalAccessException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (InvocationTargetException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
}
} else {
String unsupportedConstraintClassName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName);
if (unsupportedConstraintClassName != null) {
return new UnsupportedConstraintDefinition(unsupportedConstraintClassName);
} else {
throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
}
}
}
}

@ -1,236 +0,0 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.ejml.simple.SimpleMatrix;
import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.math.DQuaternion;
import com.jme3.scene.plugins.blender.math.DTransform;
import com.jme3.scene.plugins.blender.math.Matrix;
import com.jme3.scene.plugins.blender.math.Vector3d;
/**
* A definiotion of a Inverse Kinematics constraint. This implementation uses Jacobian pseudoinverse algorithm.
*
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintDefinitionIK extends ConstraintDefinition {
private static final float MIN_DISTANCE = 0.001f;
private static final float MIN_ANGLE_CHANGE = 0.001f;
private static final int FLAG_USE_TAIL = 0x01;
private static final int FLAG_POSITION = 0x20;
private BonesChain bones;
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
private int bonesAffected;
/** Indicates if the tail of the bone should be used or not. */
private boolean useTail;
/** The amount of iterations of the algorithm. */
private int iterations;
/** The count of bones' chain. */
private int bonesCount = -1;
public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext);
bonesAffected = ((Number) constraintData.getFieldValue("rootbone")).intValue();
iterations = ((Number) constraintData.getFieldValue("iterations")).intValue();
useTail = (flag & FLAG_USE_TAIL) != 0;
if ((flag & FLAG_POSITION) == 0) {
trackToBeChanged = false;
}
if (trackToBeChanged) {
alteredOmas = new HashSet<Long>();
}
}
/**
* Below are the variables that only need to be allocated once for IK constraint instance.
*/
/** Temporal quaternion. */
private DQuaternion tempDQuaternion = new DQuaternion();
/** Temporal matrix column. */
private Vector3d col = new Vector3d();
/** Effector's position change. */
private Matrix deltaP = new Matrix(3, 1);
/** The current target position. */
private Vector3d target = new Vector3d();
/** Rotation vectors for each joint (allocated when we know the size of a bones' chain. */
private Vector3d[] rotationVectors;
/** The Jacobian matrix. Allocated when the bones' chain size is known. */
private Matrix J;
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !trackToBeChanged || targetTransform == null || bonesCount == 0) {
return;// no need to do anything
}
if (bones == null) {
bones = new BonesChain((Bone) this.getOwner(), useTail, bonesAffected, alteredOmas, blenderContext);
}
if (bones.size() == 0) {
bonesCount = 0;
return;// no need to do anything
}
double distanceFromTarget = Double.MAX_VALUE;
target.set(targetTransform.getTranslation().x, targetTransform.getTranslation().y, targetTransform.getTranslation().z);
if (bonesCount < 0) {
bonesCount = bones.size();
rotationVectors = new Vector3d[bonesCount];
for (int i = 0; i < bonesCount; ++i) {
rotationVectors[i] = new Vector3d();
}
J = new Matrix(3, bonesCount);
}
BoneContext topBone = bones.get(0);
for (int i = 0; i < iterations; ++i) {
DTransform topBoneTransform = bones.getWorldTransform(topBone);
Vector3d e = topBoneTransform.getTranslation().add(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
distanceFromTarget = e.distance(target);
if (distanceFromTarget <= MIN_DISTANCE) {
break;
}
deltaP.setColumn(0, 0, target.x - e.x, target.y - e.y, target.z - e.z);
int column = 0;
for (BoneContext boneContext : bones) {
DTransform boneWorldTransform = bones.getWorldTransform(boneContext);
Vector3d j = boneWorldTransform.getTranslation(); // current join position
Vector3d vectorFromJointToEffector = e.subtract(j);
vectorFromJointToEffector.cross(target.subtract(j), rotationVectors[column]).normalizeLocal();
rotationVectors[column].cross(vectorFromJointToEffector, col);
J.setColumn(col, column++);
}
Matrix J_1 = J.pseudoinverse();
SimpleMatrix deltaThetas = J_1.mult(deltaP);
if (deltaThetas.elementMaxAbs() < MIN_ANGLE_CHANGE) {
break;
}
for (int j = 0; j < deltaThetas.numRows(); ++j) {
double angle = deltaThetas.get(j, 0);
Vector3d rotationVector = rotationVectors[j];
tempDQuaternion.fromAngleAxis(angle, rotationVector);
BoneContext boneContext = bones.get(j);
Bone bone = boneContext.getBone();
if (bone.equals(this.getOwner())) {
if (boneContext.isLockX()) {
tempDQuaternion.set(0, tempDQuaternion.getY(), tempDQuaternion.getZ(), tempDQuaternion.getW());
}
if (boneContext.isLockY()) {
tempDQuaternion.set(tempDQuaternion.getX(), 0, tempDQuaternion.getZ(), tempDQuaternion.getW());
}
if (boneContext.isLockZ()) {
tempDQuaternion.set(tempDQuaternion.getX(), tempDQuaternion.getY(), 0, tempDQuaternion.getW());
}
}
DTransform boneTransform = bones.getWorldTransform(boneContext);
boneTransform.getRotation().set(tempDQuaternion.mult(boneTransform.getRotation()));
bones.setWorldTransform(boneContext, boneTransform);
}
}
// applying the results
for (int i = bonesCount - 1; i >= 0; --i) {
BoneContext boneContext = bones.get(i);
DTransform transform = bones.getWorldTransform(boneContext);
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform.toTransform());
}
bones = null;// need to reload them again
}
@Override
public String getConstraintTypeName() {
return "Inverse kinematics";
}
@Override
public boolean isTargetRequired() {
return true;
}
/**
* Loaded bones' chain. This class allows to operate on transform matrices that use double precision in computations.
* Only the final result is being transformed to single precision numbers.
*
* @author Marcin Roguski (Kaelthas)
*/
private static class BonesChain extends ArrayList<BoneContext> {
private static final long serialVersionUID = -1850524345643600718L;
private List<Matrix> localBonesMatrices = new ArrayList<Matrix>();
public BonesChain(Bone bone, boolean useTail, int bonesAffected, Collection<Long> alteredOmas, BlenderContext blenderContext) {
if (bone != null) {
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
if (!useTail) {
bone = bone.getParent();
}
while (bone != null && (bonesAffected <= 0 || this.size() < bonesAffected)) {
BoneContext boneContext = blenderContext.getBoneContext(bone);
this.add(boneContext);
alteredOmas.add(boneContext.getBoneOma());
Transform transform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
localBonesMatrices.add(new DTransform(transform).toMatrix());
bone = bone.getParent();
}
if(localBonesMatrices.size() > 0) {
// making the matrices describe the local transformation
Matrix parentWorldMatrix = localBonesMatrices.get(localBonesMatrices.size() - 1);
for(int i=localBonesMatrices.size() - 2;i>=0;--i) {
SimpleMatrix m = parentWorldMatrix.invert().mult(localBonesMatrices.get(i));
parentWorldMatrix = localBonesMatrices.get(i);
localBonesMatrices.set(i, new Matrix(m));
}
}
}
}
public DTransform getWorldTransform(BoneContext bone) {
int index = this.indexOf(bone);
return this.getWorldMatrix(index).toTransform();
}
public void setWorldTransform(BoneContext bone, DTransform transform) {
int index = this.indexOf(bone);
Matrix boneMatrix = transform.toMatrix();
if (index < this.size() - 1) {
// computing the current bone local transform
Matrix parentWorldMatrix = this.getWorldMatrix(index + 1);
SimpleMatrix m = parentWorldMatrix.invert().mult(boneMatrix);
boneMatrix = new Matrix(m);
}
localBonesMatrices.set(index, boneMatrix);
}
public Matrix getWorldMatrix(int index) {
if (index == this.size() - 1) {
return new Matrix(localBonesMatrices.get(this.size() - 1));
}
SimpleMatrix result = this.getWorldMatrix(index + 1);
result = result.mult(localBonesMatrices.get(index));
return new Matrix(result);
}
}
}

@ -1,107 +0,0 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* This class represents 'Loc like' constraint type in blender.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class ConstraintDefinitionLocLike extends ConstraintDefinition {
private static final int LOCLIKE_X = 0x01;
private static final int LOCLIKE_Y = 0x02;
private static final int LOCLIKE_Z = 0x04;
// protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in
// blender
private static final int LOCLIKE_X_INVERT = 0x10;
private static final int LOCLIKE_Y_INVERT = 0x20;
private static final int LOCLIKE_Z_INVERT = 0x40;
private static final int LOCLIKE_OFFSET = 0x80;
public ConstraintDefinitionLocLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext);
if (blenderContext.getBlenderKey().isFixUpAxis()) {
// swapping Y and X limits flag in the bitwise flag
int y = flag & LOCLIKE_Y;
int invY = flag & LOCLIKE_Y_INVERT;
int z = flag & LOCLIKE_Z;
int invZ = flag & LOCLIKE_Z_INVERT;
// clear the other flags to swap them
flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;
flag |= y << 1;
flag |= invY << 1;
flag |= z >> 1;
flag |= invZ >> 1;
trackToBeChanged = (flag & LOCLIKE_X) != 0 || (flag & LOCLIKE_Y) != 0 || (flag & LOCLIKE_Z) != 0;
}
}
@Override
public boolean isTrackToBeChanged() {
// location copy does not work on bones who are connected to their parent
return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT));
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || targetTransform == null || !this.isTrackToBeChanged()) {
return;
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f ownerLocation = ownerTransform.getTranslation();
Vector3f targetLocation = targetTransform.getTranslation();
Vector3f startLocation = ownerTransform.getTranslation().clone();
Vector3f offset = Vector3f.ZERO;
if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original location to the copied location
offset = startLocation;
}
if ((flag & LOCLIKE_X) != 0) {
ownerLocation.x = targetLocation.x;
if ((flag & LOCLIKE_X_INVERT) != 0) {
ownerLocation.x = -ownerLocation.x;
}
}
if ((flag & LOCLIKE_Y) != 0) {
ownerLocation.y = targetLocation.y;
if ((flag & LOCLIKE_Y_INVERT) != 0) {
ownerLocation.y = -ownerLocation.y;
}
}
if ((flag & LOCLIKE_Z) != 0) {
ownerLocation.z = targetLocation.z;
if ((flag & LOCLIKE_Z_INVERT) != 0) {
ownerLocation.z = -ownerLocation.z;
}
}
ownerLocation.addLocal(offset);
if (influence < 1.0f) {
startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
ownerLocation.addLocal(startLocation);
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@Override
public String getConstraintTypeName() {
return "Copy location";
}
@Override
public boolean isTargetRequired() {
return true;
}
}

@ -1,106 +0,0 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* This class represents 'Loc limit' constraint type in blender.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class ConstraintDefinitionLocLimit extends ConstraintDefinition {
private static final int LIMIT_XMIN = 0x01;
private static final int LIMIT_XMAX = 0x02;
private static final int LIMIT_YMIN = 0x04;
private static final int LIMIT_YMAX = 0x08;
private static final int LIMIT_ZMIN = 0x10;
private static final int LIMIT_ZMAX = 0x20;
protected float[][] limits = new float[3][2];
public ConstraintDefinitionLocLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext);
if (blenderContext.getBlenderKey().isFixUpAxis()) {
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
// swapping Y and X limits flag in the bitwise flag
int ymin = flag & LIMIT_YMIN;
int ymax = flag & LIMIT_YMAX;
int zmin = flag & LIMIT_ZMIN;
int zmax = flag & LIMIT_ZMAX;
flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap
// them
flag |= ymin << 2;
flag |= ymax << 2;
flag |= zmin >> 2;
flag |= zmax >> 2;
} else {
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
limits[1][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
limits[1][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
}
trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0;
}
@Override
public boolean isTrackToBeChanged() {
// location limit does not work on bones who are connected to their parent
return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT));
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !this.isTrackToBeChanged()) {
return;// no need to do anything
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f translation = ownerTransform.getTranslation();
if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
translation.x -= (translation.x - limits[0][0]) * influence;
}
if ((flag & LIMIT_XMAX) != 0 && translation.x > limits[0][1]) {
translation.x -= (translation.x - limits[0][1]) * influence;
}
if ((flag & LIMIT_YMIN) != 0 && translation.y < limits[1][0]) {
translation.y -= (translation.y - limits[1][0]) * influence;
}
if ((flag & LIMIT_YMAX) != 0 && translation.y > limits[1][1]) {
translation.y -= (translation.y - limits[1][1]) * influence;
}
if ((flag & LIMIT_ZMIN) != 0 && translation.z < limits[2][0]) {
translation.z -= (translation.z - limits[2][0]) * influence;
}
if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
translation.z -= (translation.z - limits[2][1]) * influence;
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@Override
public String getConstraintTypeName() {
return "Limit location";
}
@Override
public boolean isTargetRequired() {
return false;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save