aboutsummaryrefslogtreecommitdiffstats
path: root/ardor3d-extras
diff options
context:
space:
mode:
authorneothemachine <[email protected]>2013-02-21 16:40:08 +0100
committerneothemachine <[email protected]>2013-02-21 16:40:08 +0100
commitf774bd917e9dfd8cf46e5ed66d41aad64477a238 (patch)
tree8d33aa0cf24165710d420c677f3336641223cff8 /ardor3d-extras
parentedbc5c757c66d56eb2cffffa4076f0f2bfd6e748 (diff)
normalized line endings
see http://www.hanselman.com/blog/YoureJustAnotherCarriageReturnLineFeedInTheWall.aspx and https://help.github.com/articles/dealing-with-line-endings
Diffstat (limited to 'ardor3d-extras')
-rw-r--r--ardor3d-extras/.settings/org.eclipse.jdt.ui.prefs218
-rw-r--r--ardor3d-extras/pom.xml64
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java46
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java484
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java56
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java88
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java98
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java100
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java86
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java394
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java682
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java30
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java376
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java460
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java480
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java602
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java696
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java368
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java104
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java72
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java156
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java840
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java380
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java1526
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java72
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java90
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java646
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java52
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java2594
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java1128
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java124
-rw-r--r--ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java162
32 files changed, 6637 insertions, 6637 deletions
diff --git a/ardor3d-extras/.settings/org.eclipse.jdt.ui.prefs b/ardor3d-extras/.settings/org.eclipse.jdt.ui.prefs
index 89b3b50..741ef0d 100644
--- a/ardor3d-extras/.settings/org.eclipse.jdt.ui.prefs
+++ b/ardor3d-extras/.settings/org.eclipse.jdt.ui.prefs
@@ -1,109 +1,109 @@
-cleanup.add_default_serial_version_id=true
-cleanup.add_generated_serial_version_id=false
-cleanup.add_missing_annotations=true
-cleanup.add_missing_deprecated_annotations=true
-cleanup.add_missing_methods=true
-cleanup.add_missing_nls_tags=false
-cleanup.add_missing_override_annotations=true
-cleanup.add_serial_version_id=true
-cleanup.always_use_blocks=true
-cleanup.always_use_parentheses_in_expressions=true
-cleanup.always_use_this_for_non_static_field_access=false
-cleanup.always_use_this_for_non_static_method_access=false
-cleanup.convert_to_enhanced_for_loop=false
-cleanup.correct_indentation=true
-cleanup.format_source_code=true
-cleanup.format_source_code_changes_only=false
-cleanup.make_local_variable_final=true
-cleanup.make_parameters_final=true
-cleanup.make_private_fields_final=true
-cleanup.make_type_abstract_if_missing_method=false
-cleanup.make_variable_declarations_final=true
-cleanup.never_use_blocks=false
-cleanup.never_use_parentheses_in_expressions=false
-cleanup.organize_imports=true
-cleanup.qualify_static_field_accesses_with_declaring_class=false
-cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
-cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-cleanup.qualify_static_member_accesses_with_declaring_class=true
-cleanup.qualify_static_method_accesses_with_declaring_class=false
-cleanup.remove_private_constructors=true
-cleanup.remove_trailing_whitespaces=true
-cleanup.remove_trailing_whitespaces_all=true
-cleanup.remove_trailing_whitespaces_ignore_empty=false
-cleanup.remove_unnecessary_casts=true
-cleanup.remove_unnecessary_nls_tags=true
-cleanup.remove_unused_imports=true
-cleanup.remove_unused_local_variables=false
-cleanup.remove_unused_private_fields=true
-cleanup.remove_unused_private_members=false
-cleanup.remove_unused_private_methods=true
-cleanup.remove_unused_private_types=true
-cleanup.sort_members=false
-cleanup.sort_members_all=false
-cleanup.use_blocks=true
-cleanup.use_blocks_only_for_return_and_throw=false
-cleanup.use_parentheses_in_expressions=false
-cleanup.use_this_for_non_static_field_access=true
-cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-cleanup.use_this_for_non_static_method_access=true
-cleanup.use_this_for_non_static_method_access_only_if_necessary=true
-cleanup_profile=_ArdorLabs
-cleanup_settings_version=2
-eclipse.preferences.version=1
-editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_ArdorLabs
-formatter_settings_version=11
-org.eclipse.jdt.ui.javadoc=false
-org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return the ${bare_field_name}\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} the ${bare_field_name} to set\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * \r\n */</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template><template autoinsert\="false" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">/**\r\n * Copyright (c) 2008-2012 Ardor Labs, Inc.\r\n *\r\n * This file is part of Ardor3D.\r\n *\r\n * Ardor3D is free software\: you can redistribute it and/or modify it \r\n * under the terms of its license which may be found in the accompanying\r\n * LICENSE file or at &lt;http\://www.ardor3d.com/LICENSE&gt;.\r\n */\r\n\r\n${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter function" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return the ${bare_field_name}\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter function" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} the ${bare_field_name} to set\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created JavaScript files" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for vars" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding function" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding functions" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-JSDoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate functions" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created function stubs" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated function stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
-sp_cleanup.add_default_serial_version_id=true
-sp_cleanup.add_generated_serial_version_id=false
-sp_cleanup.add_missing_annotations=true
-sp_cleanup.add_missing_deprecated_annotations=true
-sp_cleanup.add_missing_methods=false
-sp_cleanup.add_missing_nls_tags=false
-sp_cleanup.add_missing_override_annotations=true
-sp_cleanup.add_serial_version_id=false
-sp_cleanup.always_use_blocks=true
-sp_cleanup.always_use_parentheses_in_expressions=false
-sp_cleanup.always_use_this_for_non_static_field_access=false
-sp_cleanup.always_use_this_for_non_static_method_access=false
-sp_cleanup.convert_to_enhanced_for_loop=false
-sp_cleanup.correct_indentation=true
-sp_cleanup.format_source_code=true
-sp_cleanup.format_source_code_changes_only=false
-sp_cleanup.make_local_variable_final=true
-sp_cleanup.make_parameters_final=true
-sp_cleanup.make_private_fields_final=true
-sp_cleanup.make_type_abstract_if_missing_method=false
-sp_cleanup.make_variable_declarations_final=true
-sp_cleanup.never_use_blocks=false
-sp_cleanup.never_use_parentheses_in_expressions=true
-sp_cleanup.on_save_use_additional_actions=true
-sp_cleanup.organize_imports=true
-sp_cleanup.qualify_static_field_accesses_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
-sp_cleanup.qualify_static_method_accesses_with_declaring_class=true
-sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=true
-sp_cleanup.remove_trailing_whitespaces_all=true
-sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
-sp_cleanup.remove_unnecessary_casts=true
-sp_cleanup.remove_unnecessary_nls_tags=true
-sp_cleanup.remove_unused_imports=true
-sp_cleanup.remove_unused_local_variables=false
-sp_cleanup.remove_unused_private_fields=true
-sp_cleanup.remove_unused_private_members=false
-sp_cleanup.remove_unused_private_methods=true
-sp_cleanup.remove_unused_private_types=true
-sp_cleanup.sort_members=false
-sp_cleanup.sort_members_all=false
-sp_cleanup.use_blocks=true
-sp_cleanup.use_blocks_only_for_return_and_throw=false
-sp_cleanup.use_parentheses_in_expressions=true
-sp_cleanup.use_this_for_non_static_field_access=true
-sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-sp_cleanup.use_this_for_non_static_method_access=true
-sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=true
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=true
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=true
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=true
+cleanup.format_source_code=true
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=true
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=true
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=false
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=true
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=true
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=_ArdorLabs
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_ArdorLabs
+formatter_settings_version=11
+org.eclipse.jdt.ui.javadoc=false
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return the ${bare_field_name}\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} the ${bare_field_name} to set\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * \r\n */</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template><template autoinsert\="false" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">/**\r\n * Copyright (c) 2008-2012 Ardor Labs, Inc.\r\n *\r\n * This file is part of Ardor3D.\r\n *\r\n * Ardor3D is free software\: you can redistribute it and/or modify it \r\n * under the terms of its license which may be found in the accompanying\r\n * LICENSE file or at &lt;http\://www.ardor3d.com/LICENSE&gt;.\r\n */\r\n\r\n${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter function" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return the ${bare_field_name}\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter function" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} the ${bare_field_name} to set\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created JavaScript files" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for vars" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding function" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding functions" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-JSDoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate functions" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created function stubs" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated function stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.wst.jsdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=true
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=true
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=true
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=true
+sp_cleanup.use_this_for_non_static_field_access=true
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=true
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/ardor3d-extras/pom.xml b/ardor3d-extras/pom.xml
index 97b6c1e..d85defc 100644
--- a/ardor3d-extras/pom.xml
+++ b/ardor3d-extras/pom.xml
@@ -1,32 +1,32 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.ardor3d</groupId>
- <artifactId>ardor3d</artifactId>
- <version>0.9-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <artifactId>ardor3d-extras</artifactId>
- <packaging>bundle</packaging>
- <name>Ardor 3D Extras Package</name>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>ardor3d-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
-</project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.ardor3d</groupId>
+ <artifactId>ardor3d</artifactId>
+ <version>0.9-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>ardor3d-extras</artifactId>
+ <packaging>bundle</packaging>
+ <name>Ardor 3D Extras Package</name>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ardor3d-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java
index 4c2d5a2..c45b4d6 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/atlas/AtlasTextureParameter.java
@@ -1,24 +1,24 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.atlas;
-
-import com.ardor3d.image.Texture.ApplyMode;
-import com.ardor3d.image.Texture.MagnificationFilter;
-import com.ardor3d.image.Texture.MinificationFilter;
-import com.ardor3d.image.Texture.WrapMode;
-
-public class AtlasTextureParameter {
- public MinificationFilter minificationFilter = MinificationFilter.Trilinear;
- public MagnificationFilter magnificationFilter = MagnificationFilter.Bilinear;
- public WrapMode wrapMode = WrapMode.EdgeClamp;
- public ApplyMode applyMode = ApplyMode.Modulate;
- public boolean compress = false;
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.atlas;
+
+import com.ardor3d.image.Texture.ApplyMode;
+import com.ardor3d.image.Texture.MagnificationFilter;
+import com.ardor3d.image.Texture.MinificationFilter;
+import com.ardor3d.image.Texture.WrapMode;
+
+public class AtlasTextureParameter {
+ public MinificationFilter minificationFilter = MinificationFilter.Trilinear;
+ public MagnificationFilter magnificationFilter = MagnificationFilter.Bilinear;
+ public WrapMode wrapMode = WrapMode.EdgeClamp;
+ public ApplyMode applyMode = ApplyMode.Modulate;
+ public boolean compress = false;
} \ No newline at end of file
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java
index 68cb450..e49ad25 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/InteractManager.java
@@ -1,242 +1,242 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact;
-
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.data.SpatialState;
-import com.ardor3d.extension.interact.filter.UpdateFilter;
-import com.ardor3d.extension.interact.widget.AbstractInteractWidget;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.input.PhysicalLayer;
-import com.ardor3d.input.logical.BasicTriggersApplier;
-import com.ardor3d.input.logical.InputTrigger;
-import com.ardor3d.input.logical.LogicalLayer;
-import com.ardor3d.input.logical.TriggerAction;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.util.ReadOnlyTimer;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
-
-public class InteractManager {
-
- /**
- * List of widgets currently managed by this manager.
- */
- protected final List<AbstractInteractWidget> _widgets = Lists.newArrayList();
-
- /**
- * The logical layer used by this manager to receive input events prior to forwarding them to the scene.
- */
- protected final LogicalLayer _logicalLayer = new LogicalLayer();
-
- /**
- * Internal flag indicating whether the last input event was consumed by the manager. This is used to decide if we
- * will forward the event to the next LogicalLayer.
- */
- protected AtomicBoolean _inputConsumed = new AtomicBoolean(false);
-
- /**
- * The widget currently active.
- */
- protected AbstractInteractWidget _activeWidget;
-
- /**
- * The current Spatial being targeted for interaction.
- */
- protected Spatial _spatialTarget;
-
- /**
- * Spatial state tracking.
- */
- protected SpatialState _state = new SpatialState();
-
- /**
- * List of filters to modify state prior to applying to a Spatial target.
- */
- protected List<UpdateFilter> _filters = Lists.newArrayList();
-
- public InteractManager() {
- setupLogicalLayer();
- }
-
- public void update(final ReadOnlyTimer timer) {
- for (final AbstractInteractWidget widget : _widgets) {
- if (!widget.isActiveUpdateOnly() || widget == _activeWidget) {
- widget.update(timer, this);
- }
- }
- }
-
- public void render(final Renderer renderer) {
- for (final AbstractInteractWidget widget : _widgets) {
- if (!widget.isActiveRenderOnly() || widget == _activeWidget) {
- widget.render(renderer, this);
- }
- }
- }
-
- protected void offerInputToWidgets(final Canvas source, final TwoInputStates inputStates) {
- if (_activeWidget != null) {
- _activeWidget.processInput(source, inputStates, _inputConsumed, this);
- }
-
- if (!_inputConsumed.get()) {
- for (final AbstractInteractWidget widget : _widgets) {
- if (widget != _activeWidget && !widget.isActiveInputOnly()) {
- widget.processInput(source, inputStates, _inputConsumed, this);
- if (_inputConsumed.get()) {
- break;
- }
- }
- }
- }
-
- if (_spatialTarget != null && _inputConsumed.get()) {
- // apply any filters to our state
- for (final UpdateFilter filter : _filters) {
- filter.applyFilter(this);
- }
-
- // apply state to target
- _state.applyState(_spatialTarget);
-
- // fire update event
- fireTargetDataUpdated();
- }
- }
-
- /**
- * Set up our logical layer with a trigger that hands input to the manager and saves whether it was "consumed".
- */
- private void setupLogicalLayer() {
- _logicalLayer.registerTrigger(new InputTrigger(new Predicate<TwoInputStates>() {
- public boolean apply(final TwoInputStates arg0) {
- // always trigger this.
- return true;
- }
- }, new TriggerAction() {
- public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
- if (_spatialTarget != null) {
- _state.getTransform().set(_spatialTarget.getTransform());
- }
- _inputConsumed.set(false);
- offerInputToWidgets(source, inputStates);
- }
- }));
- }
-
- /**
- * Convenience method for setting up the manager's connection to the Ardor3D input system, along with a forwarding
- * address for input events that the manager does not care about.
- *
- * @param canvas
- * the canvas to register with
- * @param physicalLayer
- * the physical layer to register with
- * @param forwardTo
- * a LogicalLayer to send unconsumed (by the manager) input events to.
- */
- public void setupInput(final Canvas canvas, final PhysicalLayer physicalLayer, final LogicalLayer forwardTo) {
- // Set up this logical layer to listen for events from the given canvas and PhysicalLayer
- _logicalLayer.registerInput(canvas, physicalLayer);
-
- // Set up forwarding for events not consumed.
- if (forwardTo != null) {
- _logicalLayer.setApplier(new BasicTriggersApplier() {
-
- @Override
- public void checkAndPerformTriggers(final Set<InputTrigger> triggers, final Canvas source,
- final TwoInputStates states, final double tpf) {
- super.checkAndPerformTriggers(triggers, source, states, tpf);
-
- if (!_inputConsumed.get()) {
- // nothing consumed
- forwardTo.getApplier().checkAndPerformTriggers(forwardTo.getTriggers(), source, states, tpf);
- } else {
- // consumed, do nothing.
- }
- }
- });
- }
- }
-
- public void addWidget(final AbstractInteractWidget widget) {
- _widgets.add(widget);
- }
-
- public void removeWidget(final AbstractInteractWidget widget) {
- if (_activeWidget == widget) {
- _activeWidget = _widgets.isEmpty() ? null : _widgets.get(0);
- }
- _widgets.remove(widget);
- }
-
- public void clearWidgets() {
- _widgets.clear();
- }
-
- public void addFilter(final UpdateFilter filter) {
- _filters.add(filter);
- }
-
- public void removeFilter(final UpdateFilter filter) {
- _filters.remove(filter);
- }
-
- public void clearFilters() {
- _filters.clear();
- }
-
- public LogicalLayer getLogicalLayer() {
- return _logicalLayer;
- }
-
- public void setActiveWidget(final AbstractInteractWidget widget) {
- _activeWidget = widget;
- }
-
- public AbstractInteractWidget getActiveWidget() {
- return _activeWidget;
- }
-
- public void setSpatialTarget(final Spatial target) {
- if (_spatialTarget != target) {
- _spatialTarget = target;
- fireTargetChanged();
- }
- }
-
- public void fireTargetChanged() {
- for (final AbstractInteractWidget widget : _widgets) {
- widget.targetChanged(this);
- }
- }
-
- public void fireTargetDataUpdated() {
- for (final AbstractInteractWidget widget : _widgets) {
- widget.targetDataUpdated(this);
- }
- }
-
- public Spatial getSpatialTarget() {
- return _spatialTarget;
- }
-
- public SpatialState getSpatialState() {
- return _state;
- }
-
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.data.SpatialState;
+import com.ardor3d.extension.interact.filter.UpdateFilter;
+import com.ardor3d.extension.interact.widget.AbstractInteractWidget;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.input.PhysicalLayer;
+import com.ardor3d.input.logical.BasicTriggersApplier;
+import com.ardor3d.input.logical.InputTrigger;
+import com.ardor3d.input.logical.LogicalLayer;
+import com.ardor3d.input.logical.TriggerAction;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.util.ReadOnlyTimer;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+
+public class InteractManager {
+
+ /**
+ * List of widgets currently managed by this manager.
+ */
+ protected final List<AbstractInteractWidget> _widgets = Lists.newArrayList();
+
+ /**
+ * The logical layer used by this manager to receive input events prior to forwarding them to the scene.
+ */
+ protected final LogicalLayer _logicalLayer = new LogicalLayer();
+
+ /**
+ * Internal flag indicating whether the last input event was consumed by the manager. This is used to decide if we
+ * will forward the event to the next LogicalLayer.
+ */
+ protected AtomicBoolean _inputConsumed = new AtomicBoolean(false);
+
+ /**
+ * The widget currently active.
+ */
+ protected AbstractInteractWidget _activeWidget;
+
+ /**
+ * The current Spatial being targeted for interaction.
+ */
+ protected Spatial _spatialTarget;
+
+ /**
+ * Spatial state tracking.
+ */
+ protected SpatialState _state = new SpatialState();
+
+ /**
+ * List of filters to modify state prior to applying to a Spatial target.
+ */
+ protected List<UpdateFilter> _filters = Lists.newArrayList();
+
+ public InteractManager() {
+ setupLogicalLayer();
+ }
+
+ public void update(final ReadOnlyTimer timer) {
+ for (final AbstractInteractWidget widget : _widgets) {
+ if (!widget.isActiveUpdateOnly() || widget == _activeWidget) {
+ widget.update(timer, this);
+ }
+ }
+ }
+
+ public void render(final Renderer renderer) {
+ for (final AbstractInteractWidget widget : _widgets) {
+ if (!widget.isActiveRenderOnly() || widget == _activeWidget) {
+ widget.render(renderer, this);
+ }
+ }
+ }
+
+ protected void offerInputToWidgets(final Canvas source, final TwoInputStates inputStates) {
+ if (_activeWidget != null) {
+ _activeWidget.processInput(source, inputStates, _inputConsumed, this);
+ }
+
+ if (!_inputConsumed.get()) {
+ for (final AbstractInteractWidget widget : _widgets) {
+ if (widget != _activeWidget && !widget.isActiveInputOnly()) {
+ widget.processInput(source, inputStates, _inputConsumed, this);
+ if (_inputConsumed.get()) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (_spatialTarget != null && _inputConsumed.get()) {
+ // apply any filters to our state
+ for (final UpdateFilter filter : _filters) {
+ filter.applyFilter(this);
+ }
+
+ // apply state to target
+ _state.applyState(_spatialTarget);
+
+ // fire update event
+ fireTargetDataUpdated();
+ }
+ }
+
+ /**
+ * Set up our logical layer with a trigger that hands input to the manager and saves whether it was "consumed".
+ */
+ private void setupLogicalLayer() {
+ _logicalLayer.registerTrigger(new InputTrigger(new Predicate<TwoInputStates>() {
+ public boolean apply(final TwoInputStates arg0) {
+ // always trigger this.
+ return true;
+ }
+ }, new TriggerAction() {
+ public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
+ if (_spatialTarget != null) {
+ _state.getTransform().set(_spatialTarget.getTransform());
+ }
+ _inputConsumed.set(false);
+ offerInputToWidgets(source, inputStates);
+ }
+ }));
+ }
+
+ /**
+ * Convenience method for setting up the manager's connection to the Ardor3D input system, along with a forwarding
+ * address for input events that the manager does not care about.
+ *
+ * @param canvas
+ * the canvas to register with
+ * @param physicalLayer
+ * the physical layer to register with
+ * @param forwardTo
+ * a LogicalLayer to send unconsumed (by the manager) input events to.
+ */
+ public void setupInput(final Canvas canvas, final PhysicalLayer physicalLayer, final LogicalLayer forwardTo) {
+ // Set up this logical layer to listen for events from the given canvas and PhysicalLayer
+ _logicalLayer.registerInput(canvas, physicalLayer);
+
+ // Set up forwarding for events not consumed.
+ if (forwardTo != null) {
+ _logicalLayer.setApplier(new BasicTriggersApplier() {
+
+ @Override
+ public void checkAndPerformTriggers(final Set<InputTrigger> triggers, final Canvas source,
+ final TwoInputStates states, final double tpf) {
+ super.checkAndPerformTriggers(triggers, source, states, tpf);
+
+ if (!_inputConsumed.get()) {
+ // nothing consumed
+ forwardTo.getApplier().checkAndPerformTriggers(forwardTo.getTriggers(), source, states, tpf);
+ } else {
+ // consumed, do nothing.
+ }
+ }
+ });
+ }
+ }
+
+ public void addWidget(final AbstractInteractWidget widget) {
+ _widgets.add(widget);
+ }
+
+ public void removeWidget(final AbstractInteractWidget widget) {
+ if (_activeWidget == widget) {
+ _activeWidget = _widgets.isEmpty() ? null : _widgets.get(0);
+ }
+ _widgets.remove(widget);
+ }
+
+ public void clearWidgets() {
+ _widgets.clear();
+ }
+
+ public void addFilter(final UpdateFilter filter) {
+ _filters.add(filter);
+ }
+
+ public void removeFilter(final UpdateFilter filter) {
+ _filters.remove(filter);
+ }
+
+ public void clearFilters() {
+ _filters.clear();
+ }
+
+ public LogicalLayer getLogicalLayer() {
+ return _logicalLayer;
+ }
+
+ public void setActiveWidget(final AbstractInteractWidget widget) {
+ _activeWidget = widget;
+ }
+
+ public AbstractInteractWidget getActiveWidget() {
+ return _activeWidget;
+ }
+
+ public void setSpatialTarget(final Spatial target) {
+ if (_spatialTarget != target) {
+ _spatialTarget = target;
+ fireTargetChanged();
+ }
+ }
+
+ public void fireTargetChanged() {
+ for (final AbstractInteractWidget widget : _widgets) {
+ widget.targetChanged(this);
+ }
+ }
+
+ public void fireTargetDataUpdated() {
+ for (final AbstractInteractWidget widget : _widgets) {
+ widget.targetDataUpdated(this);
+ }
+ }
+
+ public Spatial getSpatialTarget() {
+ return _spatialTarget;
+ }
+
+ public SpatialState getSpatialState() {
+ return _state;
+ }
+
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java
index d96ab5a..4ba58ea 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/data/SpatialState.java
@@ -1,28 +1,28 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.data;
-
-import com.ardor3d.math.Transform;
-import com.ardor3d.scenegraph.Spatial;
-
-public class SpatialState {
-
- protected Transform _transform = new Transform();
-
- public Transform getTransform() {
- return _transform;
- }
-
- public void applyState(final Spatial target) {
- target.setTransform(_transform);
- }
-
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.data;
+
+import com.ardor3d.math.Transform;
+import com.ardor3d.scenegraph.Spatial;
+
+public class SpatialState {
+
+ protected Transform _transform = new Transform();
+
+ public Transform getTransform() {
+ return _transform;
+ }
+
+ public void applyState(final Spatial target) {
+ target.setTransform(_transform);
+ }
+
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java
index a9798ce..d4707ac 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/AllowScaleFilter.java
@@ -1,44 +1,44 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.filter;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.extension.interact.data.SpatialState;
-import com.ardor3d.math.type.ReadOnlyVector3;
-
-public class AllowScaleFilter implements UpdateFilter {
-
- protected boolean _xAxis, _yAxis, _zAxis;
-
- public AllowScaleFilter(final boolean xAxis, final boolean yAxis, final boolean zAxis) {
- _xAxis = xAxis;
- _yAxis = yAxis;
- _zAxis = zAxis;
- }
-
- @Override
- public void applyFilter(final InteractManager manager) {
- final ReadOnlyVector3 oldScale = manager.getSpatialTarget().getScale();
- final SpatialState state = manager.getSpatialState();
- final ReadOnlyVector3 scale = state.getTransform().getScale();
-
- state.getTransform().setScale( //
- _xAxis ? scale.getX() : oldScale.getX(), //
- _yAxis ? scale.getY() : oldScale.getY(), //
- _zAxis ? scale.getZ() : oldScale.getZ());
- }
-
- @Override
- public void beginDrag(final InteractManager manager) {}
-
- @Override
- public void endDrag(final InteractManager manager) {}
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.filter;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.extension.interact.data.SpatialState;
+import com.ardor3d.math.type.ReadOnlyVector3;
+
+public class AllowScaleFilter implements UpdateFilter {
+
+ protected boolean _xAxis, _yAxis, _zAxis;
+
+ public AllowScaleFilter(final boolean xAxis, final boolean yAxis, final boolean zAxis) {
+ _xAxis = xAxis;
+ _yAxis = yAxis;
+ _zAxis = zAxis;
+ }
+
+ @Override
+ public void applyFilter(final InteractManager manager) {
+ final ReadOnlyVector3 oldScale = manager.getSpatialTarget().getScale();
+ final SpatialState state = manager.getSpatialState();
+ final ReadOnlyVector3 scale = state.getTransform().getScale();
+
+ state.getTransform().setScale( //
+ _xAxis ? scale.getX() : oldScale.getX(), //
+ _yAxis ? scale.getY() : oldScale.getY(), //
+ _zAxis ? scale.getZ() : oldScale.getZ());
+ }
+
+ @Override
+ public void beginDrag(final InteractManager manager) {}
+
+ @Override
+ public void endDrag(final InteractManager manager) {}
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java
index 7336bab..f331742 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/MinMaxScaleFilter.java
@@ -1,49 +1,49 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.filter;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.extension.interact.data.SpatialState;
-import com.ardor3d.math.MathUtils;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyVector3;
-
-public class MinMaxScaleFilter implements UpdateFilter {
- protected Vector3 _minScale = new Vector3();
- protected Vector3 _maxScale = new Vector3();
-
- public MinMaxScaleFilter(final double min, final double max) {
- _minScale.set(min, min, min);
- _maxScale.set(max, max, max);
- }
-
- public MinMaxScaleFilter(final ReadOnlyVector3 min, final ReadOnlyVector3 max) {
- _minScale.set(min);
- _maxScale.set(max);
- }
-
- @Override
- public void applyFilter(final InteractManager manager) {
- final SpatialState state = manager.getSpatialState();
- final ReadOnlyVector3 scale = state.getTransform().getScale();
- final double x = MathUtils.clamp(scale.getX(), _minScale.getX(), _maxScale.getX());
- final double y = MathUtils.clamp(scale.getY(), _minScale.getY(), _maxScale.getY());
- final double z = MathUtils.clamp(scale.getZ(), _minScale.getZ(), _maxScale.getZ());
-
- state.getTransform().setScale(x, y, z);
- }
-
- @Override
- public void beginDrag(final InteractManager manager) {}
-
- @Override
- public void endDrag(final InteractManager manager) {}
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.filter;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.extension.interact.data.SpatialState;
+import com.ardor3d.math.MathUtils;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyVector3;
+
+public class MinMaxScaleFilter implements UpdateFilter {
+ protected Vector3 _minScale = new Vector3();
+ protected Vector3 _maxScale = new Vector3();
+
+ public MinMaxScaleFilter(final double min, final double max) {
+ _minScale.set(min, min, min);
+ _maxScale.set(max, max, max);
+ }
+
+ public MinMaxScaleFilter(final ReadOnlyVector3 min, final ReadOnlyVector3 max) {
+ _minScale.set(min);
+ _maxScale.set(max);
+ }
+
+ @Override
+ public void applyFilter(final InteractManager manager) {
+ final SpatialState state = manager.getSpatialState();
+ final ReadOnlyVector3 scale = state.getTransform().getScale();
+ final double x = MathUtils.clamp(scale.getX(), _minScale.getX(), _maxScale.getX());
+ final double y = MathUtils.clamp(scale.getY(), _minScale.getY(), _maxScale.getY());
+ final double z = MathUtils.clamp(scale.getZ(), _minScale.getZ(), _maxScale.getZ());
+
+ state.getTransform().setScale(x, y, z);
+ }
+
+ @Override
+ public void beginDrag(final InteractManager manager) {}
+
+ @Override
+ public void endDrag(final InteractManager manager) {}
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java
index 00f28c5..cbff414 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/PlaneBoundaryFilter.java
@@ -1,50 +1,50 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.filter;
-
-import java.util.Arrays;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.extension.interact.data.SpatialState;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyPlane;
-
-public class PlaneBoundaryFilter implements UpdateFilter {
- private final ReadOnlyPlane[] _planes;
- private final Vector3 _calcVectorA = new Vector3();
- private final Vector3 _calcVectorB = new Vector3();
-
- public PlaneBoundaryFilter(final ReadOnlyPlane... planes) {
- _planes = Arrays.copyOf(planes, planes.length);
- }
-
- @Override
- public void applyFilter(final InteractManager manager) {
- final SpatialState state = manager.getSpatialState();
- _calcVectorA.set(state.getTransform().getTranslation());
- for (final ReadOnlyPlane plane : _planes) {
- final double distance = plane.pseudoDistance(_calcVectorA);
- if (distance < 0) {
- // push us back to the plane.
- _calcVectorB.set(plane.getNormal()).multiplyLocal(-distance);
- _calcVectorA.addLocal(_calcVectorB);
- }
- }
-
- state.getTransform().setTranslation(_calcVectorA);
- }
-
- @Override
- public void beginDrag(final InteractManager manager) {}
-
- @Override
- public void endDrag(final InteractManager manager) {}
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.filter;
+
+import java.util.Arrays;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.extension.interact.data.SpatialState;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyPlane;
+
+public class PlaneBoundaryFilter implements UpdateFilter {
+ private final ReadOnlyPlane[] _planes;
+ private final Vector3 _calcVectorA = new Vector3();
+ private final Vector3 _calcVectorB = new Vector3();
+
+ public PlaneBoundaryFilter(final ReadOnlyPlane... planes) {
+ _planes = Arrays.copyOf(planes, planes.length);
+ }
+
+ @Override
+ public void applyFilter(final InteractManager manager) {
+ final SpatialState state = manager.getSpatialState();
+ _calcVectorA.set(state.getTransform().getTranslation());
+ for (final ReadOnlyPlane plane : _planes) {
+ final double distance = plane.pseudoDistance(_calcVectorA);
+ if (distance < 0) {
+ // push us back to the plane.
+ _calcVectorB.set(plane.getNormal()).multiplyLocal(-distance);
+ _calcVectorA.addLocal(_calcVectorB);
+ }
+ }
+
+ state.getTransform().setTranslation(_calcVectorA);
+ }
+
+ @Override
+ public void beginDrag(final InteractManager manager) {}
+
+ @Override
+ public void endDrag(final InteractManager manager) {}
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java
index 5f1e336..20d1644 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/filter/UpdateFilter.java
@@ -1,43 +1,43 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.filter;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.extension.interact.data.SpatialState;
-
-/**
- * Filter used to modify {@link SpatialState} information prior to it being applied to a Spatial by the
- * {@link InteractManager}.
- */
-public interface UpdateFilter {
-
- /**
- * Called after a successful application of mouse/key input.
- *
- * @param manager
- */
- void applyFilter(InteractManager manager);
-
- /**
- * Callback for when a control begins a drag operation.
- *
- * @param manager
- */
- void beginDrag(InteractManager manager);
-
- /**
- * Callback for when a control ends a drag operation.
- *
- * @param manager
- */
- void endDrag(InteractManager manager);
-
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.filter;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.extension.interact.data.SpatialState;
+
+/**
+ * Filter used to modify {@link SpatialState} information prior to it being applied to a Spatial by the
+ * {@link InteractManager}.
+ */
+public interface UpdateFilter {
+
+ /**
+ * Called after a successful application of mouse/key input.
+ *
+ * @param manager
+ */
+ void applyFilter(InteractManager manager);
+
+ /**
+ * Callback for when a control begins a drag operation.
+ *
+ * @param manager
+ */
+ void beginDrag(InteractManager manager);
+
+ /**
+ * Callback for when a control ends a drag operation.
+ *
+ * @param manager
+ */
+ void endDrag(InteractManager manager);
+
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java
index 39fbbc5..af00b28 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/AbstractInteractWidget.java
@@ -1,197 +1,197 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.extension.interact.filter.UpdateFilter;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.input.MouseButton;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.intersection.PickingUtil;
-import com.ardor3d.intersection.PrimitivePickResults;
-import com.ardor3d.math.Ray3;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.scenegraph.Node;
-import com.ardor3d.util.ReadOnlyTimer;
-import com.google.common.collect.Lists;
-
-public abstract class AbstractInteractWidget {
-
- protected Node _handle;
- protected boolean _flipPickRay, _dragging = false;
- protected MouseButton _dragButton = MouseButton.LEFT;
-
- protected boolean _activeInputOnly = true;
- protected boolean _activeRenderOnly = true;
- protected boolean _activeUpdateOnly = true;
-
- protected Ray3 _calcRay = new Ray3();
- protected final Vector3 _calcVec3A = new Vector3();
- protected final Vector3 _calcVec3B = new Vector3();
- protected final Vector3 _calcVec3C = new Vector3();
- protected final Vector3 _calcVec3D = new Vector3();
- protected PrimitivePickResults _results = new PrimitivePickResults();
-
- protected InteractMatrix _interactMatrix = InteractMatrix.World;
-
- /**
- * List of filters to modify state after applying input.
- */
- protected List<UpdateFilter> _filters = Lists.newArrayList();
-
- public AbstractInteractWidget() {
- _results.setCheckDistance(true);
- }
-
- /**
- * Use the given inputstates to determine if and how to activate this widget. If the widget uses the given input,
- * inputConsumed should be set to "true" and applyFilters should be called by this method.
- *
- * @param source
- * the canvas that is our input source.
- * @param inputStates
- * the current and previous state of our input devices.
- * @param inputConsumed
- * an atomic boolean used to indicate back to the caller of this function that we have consumed the given
- * inputStates. If set to true, no other widgets will be offered this input, nor will any other scene
- * input triggers attached to the manager.
- * @param manager
- * our interact manager.
- */
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {}
-
- protected void applyFilters(final InteractManager manager) {
- // apply any filters to our state
- for (final UpdateFilter filter : _filters) {
- filter.applyFilter(manager);
- }
- }
-
- public void beginDrag(final InteractManager manager) {
- _dragging = true;
- for (final UpdateFilter filter : _filters) {
- filter.beginDrag(manager);
- }
- }
-
- public void endDrag(final InteractManager manager) {
- _dragging = false;
- for (final UpdateFilter filter : _filters) {
- filter.endDrag(manager);
- }
- }
-
- public void update(final ReadOnlyTimer timer, final InteractManager manager) {
- _handle.updateGeometricState(timer.getTimePerFrame());
- }
-
- public void render(final Renderer renderer, final InteractManager manager) {}
-
- public void targetChanged(final InteractManager manager) {}
-
- public void targetDataUpdated(final InteractManager manager) {}
-
- public void receivedControl(final InteractManager manager) {
- if (_dragging) {
- endDrag(manager);
- }
- }
-
- public void lostControl(final InteractManager manager) {}
-
- public boolean isActiveInputOnly() {
- return _activeInputOnly;
- }
-
- public void setActiveInputOnly(final boolean activeOnly) {
- _activeInputOnly = activeOnly;
- }
-
- public boolean isActiveRenderOnly() {
- return _activeRenderOnly;
- }
-
- public void setActiveRenderOnly(final boolean activeOnly) {
- _activeRenderOnly = activeOnly;
- }
-
- public boolean isActiveUpdateOnly() {
- return _activeUpdateOnly;
- }
-
- public void setActiveUpdateOnly(final boolean activeOnly) {
- _activeUpdateOnly = activeOnly;
- }
-
- public boolean isFlipPickRay() {
- return _flipPickRay;
- }
-
- public void setFlipPickRay(final boolean flip) {
- _flipPickRay = flip;
- }
-
- public MouseButton getDragButton() {
- return _dragButton;
- }
-
- public void setDragButton(final MouseButton button) {
- _dragButton = button;
- }
-
- public Node getHandle() {
- return _handle;
- }
-
- protected Vector3 getLastPick() {
- if (_results.getNumber() > 0 && _results.getPickData(0).getIntersectionRecord().getNumberOfIntersections() > 0) {
- return _results.getPickData(0).getIntersectionRecord().getIntersectionPoint(0);
- }
- return null;
- }
-
- protected void findPick(final Vector2 mouseLoc, final Camera camera) {
- getPickRay(mouseLoc, camera);
- _results.clear();
- PickingUtil.findPick(_handle, _calcRay, _results);
- }
-
- protected void getPickRay(final Vector2 mouseLoc, final Camera camera) {
- camera.getPickRay(mouseLoc, _flipPickRay, _calcRay);
- }
-
- public void setInteractMatrix(final InteractMatrix matrix) {
- _interactMatrix = matrix;
- }
-
- public InteractMatrix getInteractMatrix() {
- return _interactMatrix;
- }
-
- public void addFilter(final UpdateFilter filter) {
- _filters.add(filter);
- }
-
- public void removeFilter(final UpdateFilter filter) {
- _filters.remove(filter);
- }
-
- public void clearFilters() {
- _filters.clear();
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.extension.interact.filter.UpdateFilter;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.input.MouseButton;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.intersection.PickingUtil;
+import com.ardor3d.intersection.PrimitivePickResults;
+import com.ardor3d.math.Ray3;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.util.ReadOnlyTimer;
+import com.google.common.collect.Lists;
+
+public abstract class AbstractInteractWidget {
+
+ protected Node _handle;
+ protected boolean _flipPickRay, _dragging = false;
+ protected MouseButton _dragButton = MouseButton.LEFT;
+
+ protected boolean _activeInputOnly = true;
+ protected boolean _activeRenderOnly = true;
+ protected boolean _activeUpdateOnly = true;
+
+ protected Ray3 _calcRay = new Ray3();
+ protected final Vector3 _calcVec3A = new Vector3();
+ protected final Vector3 _calcVec3B = new Vector3();
+ protected final Vector3 _calcVec3C = new Vector3();
+ protected final Vector3 _calcVec3D = new Vector3();
+ protected PrimitivePickResults _results = new PrimitivePickResults();
+
+ protected InteractMatrix _interactMatrix = InteractMatrix.World;
+
+ /**
+ * List of filters to modify state after applying input.
+ */
+ protected List<UpdateFilter> _filters = Lists.newArrayList();
+
+ public AbstractInteractWidget() {
+ _results.setCheckDistance(true);
+ }
+
+ /**
+ * Use the given inputstates to determine if and how to activate this widget. If the widget uses the given input,
+ * inputConsumed should be set to "true" and applyFilters should be called by this method.
+ *
+ * @param source
+ * the canvas that is our input source.
+ * @param inputStates
+ * the current and previous state of our input devices.
+ * @param inputConsumed
+ * an atomic boolean used to indicate back to the caller of this function that we have consumed the given
+ * inputStates. If set to true, no other widgets will be offered this input, nor will any other scene
+ * input triggers attached to the manager.
+ * @param manager
+ * our interact manager.
+ */
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {}
+
+ protected void applyFilters(final InteractManager manager) {
+ // apply any filters to our state
+ for (final UpdateFilter filter : _filters) {
+ filter.applyFilter(manager);
+ }
+ }
+
+ public void beginDrag(final InteractManager manager) {
+ _dragging = true;
+ for (final UpdateFilter filter : _filters) {
+ filter.beginDrag(manager);
+ }
+ }
+
+ public void endDrag(final InteractManager manager) {
+ _dragging = false;
+ for (final UpdateFilter filter : _filters) {
+ filter.endDrag(manager);
+ }
+ }
+
+ public void update(final ReadOnlyTimer timer, final InteractManager manager) {
+ _handle.updateGeometricState(timer.getTimePerFrame());
+ }
+
+ public void render(final Renderer renderer, final InteractManager manager) {}
+
+ public void targetChanged(final InteractManager manager) {}
+
+ public void targetDataUpdated(final InteractManager manager) {}
+
+ public void receivedControl(final InteractManager manager) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ }
+
+ public void lostControl(final InteractManager manager) {}
+
+ public boolean isActiveInputOnly() {
+ return _activeInputOnly;
+ }
+
+ public void setActiveInputOnly(final boolean activeOnly) {
+ _activeInputOnly = activeOnly;
+ }
+
+ public boolean isActiveRenderOnly() {
+ return _activeRenderOnly;
+ }
+
+ public void setActiveRenderOnly(final boolean activeOnly) {
+ _activeRenderOnly = activeOnly;
+ }
+
+ public boolean isActiveUpdateOnly() {
+ return _activeUpdateOnly;
+ }
+
+ public void setActiveUpdateOnly(final boolean activeOnly) {
+ _activeUpdateOnly = activeOnly;
+ }
+
+ public boolean isFlipPickRay() {
+ return _flipPickRay;
+ }
+
+ public void setFlipPickRay(final boolean flip) {
+ _flipPickRay = flip;
+ }
+
+ public MouseButton getDragButton() {
+ return _dragButton;
+ }
+
+ public void setDragButton(final MouseButton button) {
+ _dragButton = button;
+ }
+
+ public Node getHandle() {
+ return _handle;
+ }
+
+ protected Vector3 getLastPick() {
+ if (_results.getNumber() > 0 && _results.getPickData(0).getIntersectionRecord().getNumberOfIntersections() > 0) {
+ return _results.getPickData(0).getIntersectionRecord().getIntersectionPoint(0);
+ }
+ return null;
+ }
+
+ protected void findPick(final Vector2 mouseLoc, final Camera camera) {
+ getPickRay(mouseLoc, camera);
+ _results.clear();
+ PickingUtil.findPick(_handle, _calcRay, _results);
+ }
+
+ protected void getPickRay(final Vector2 mouseLoc, final Camera camera) {
+ camera.getPickRay(mouseLoc, _flipPickRay, _calcRay);
+ }
+
+ public void setInteractMatrix(final InteractMatrix matrix) {
+ _interactMatrix = matrix;
+ }
+
+ public InteractMatrix getInteractMatrix() {
+ return _interactMatrix;
+ }
+
+ public void addFilter(final UpdateFilter filter) {
+ _filters.add(filter);
+ }
+
+ public void removeFilter(final UpdateFilter filter) {
+ _filters.remove(filter);
+ }
+
+ public void clearFilters() {
+ _filters.clear();
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java
index 2018f8c..ea41735 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/CompoundInteractWidget.java
@@ -1,341 +1,341 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.extension.interact.filter.UpdateFilter;
-import com.ardor3d.extension.interact.widget.MovePlanarWidget.MovePlane;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.image.Texture2D;
-import com.ardor3d.input.ButtonState;
-import com.ardor3d.input.MouseState;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.type.ReadOnlyColorRGBA;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.scenegraph.Node;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.util.ReadOnlyTimer;
-import com.google.common.collect.Maps;
-
-public class CompoundInteractWidget extends AbstractInteractWidget {
- private static final String MOVE_KEY = "Move";
- private static final String ROTATE_KEY = "Rotate";
- private static final String MOVE_PLANAR_KEY = "MovePlanar";
- private static final String MOVE_MULTIPLANAR_KEY = "MoveMultiPlanar";
-
- public static double MIN_SCALE = 0.000001;
-
- protected Map<String, AbstractInteractWidget> _widgets = Maps.newHashMap();
-
- protected AbstractInteractWidget _lastInputWidget = null;
-
- protected InteractMatrix _interactMatrix;
-
- public CompoundInteractWidget() {
- _handle = new Node("handleRoot");
- }
-
- @Override
- public void addFilter(final UpdateFilter filter) {
- for(final AbstractInteractWidget widget : _widgets.values()) {
- widget.addFilter(filter);
- }
- super.addFilter(filter);
- }
-
- @Override
- public void removeFilter(final UpdateFilter filter) {
- for(final AbstractInteractWidget widget : _widgets.values()) {
- widget.removeFilter(filter);
- }
- super.removeFilter(filter);
- }
-
- @Override
- public void clearFilters() {
- for(final AbstractInteractWidget widget : _widgets.values()) {
- widget.clearFilters();
- }
- super.clearFilters();
- }
-
- public CompoundInteractWidget withMoveXAxis() {
- verifyMoveWidget().withXAxis();
- return this;
- }
-
- public CompoundInteractWidget withMoveXAxis(final ReadOnlyColorRGBA color) {
- verifyMoveWidget().withXAxis(color);
- return this;
- }
-
- public CompoundInteractWidget withMoveXAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
- final double lengthGap, final double tipGap) {
- verifyMoveWidget().withXAxis(color, scale, width, lengthGap, tipGap);
- return this;
- }
-
- public CompoundInteractWidget withMoveYAxis() {
- verifyMoveWidget().withYAxis();
- return this;
- }
-
- public CompoundInteractWidget withMoveYAxis(final ReadOnlyColorRGBA color) {
- verifyMoveWidget().withYAxis(color);
- return this;
- }
-
- public CompoundInteractWidget withMoveYAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
- final double lengthGap, final double tipGap) {
- verifyMoveWidget().withYAxis(color, scale, width, lengthGap, tipGap);
- return this;
- }
-
- public CompoundInteractWidget withMoveZAxis() {
- verifyMoveWidget().withZAxis();
- return this;
- }
-
- public CompoundInteractWidget withMoveZAxis(final ReadOnlyColorRGBA color) {
- verifyMoveWidget().withZAxis(color);
- return this;
- }
-
- public CompoundInteractWidget withMoveZAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
- final double lengthGap, final double tipGap) {
- verifyMoveWidget().withZAxis(color, scale, width, lengthGap, tipGap);
- return this;
- }
-
- public CompoundInteractWidget withRotateXAxis() {
- verifyRotateWidget().withXAxis();
- return this;
- }
-
- public CompoundInteractWidget withRotateXAxis(final ReadOnlyColorRGBA color) {
- verifyRotateWidget().withXAxis(color);
- return this;
- }
-
- public CompoundInteractWidget withRotateXAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
- verifyRotateWidget().withXAxis(color, scale, width);
- return this;
- }
-
- public CompoundInteractWidget withRotateYAxis() {
- verifyRotateWidget().withYAxis();
- return this;
- }
-
- public CompoundInteractWidget withRotateYAxis(final ReadOnlyColorRGBA color) {
- verifyRotateWidget().withYAxis(color);
- return this;
- }
-
- public CompoundInteractWidget withRotateYAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
- verifyRotateWidget().withYAxis(color, scale, width);
- return this;
- }
-
- public CompoundInteractWidget withRotateZAxis() {
- verifyRotateWidget().withZAxis();
- return this;
- }
-
- public CompoundInteractWidget withRotateZAxis(final ReadOnlyColorRGBA color) {
- verifyRotateWidget().withZAxis(color);
- return this;
- }
-
- public CompoundInteractWidget withRotateZAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
- verifyRotateWidget().withZAxis(color, scale, width);
- return this;
- }
-
- public CompoundInteractWidget withRingTexture(final Texture2D texture) {
- verifyRotateWidget().setTexture(texture);
- return this;
- }
-
- public CompoundInteractWidget withMultiPlanarHandle() {
- MoveMultiPlanarWidget widget = (MoveMultiPlanarWidget) _widgets
- .get(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY);
- if (widget != null) {
- widget.getHandle().removeFromParent();
- }
-
- widget = new MoveMultiPlanarWidget();
- _widgets.put(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY, widget);
- _handle.attachChild(widget.getHandle());
-
- return this;
- }
-
- public CompoundInteractWidget withMultiPlanarHandle(final double extent) {
- MoveMultiPlanarWidget widget = (MoveMultiPlanarWidget) _widgets
- .get(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY);
- if (widget != null) {
- widget.getHandle().removeFromParent();
- }
-
- widget = new MoveMultiPlanarWidget(extent);
- _widgets.put(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY, widget);
- _handle.attachChild(widget.getHandle());
-
- return this;
- }
-
- public CompoundInteractWidget withPlanarHandle(final MovePlane plane, final ReadOnlyColorRGBA color) {
- MovePlanarWidget widget = (MovePlanarWidget) _widgets.get(CompoundInteractWidget.MOVE_PLANAR_KEY);
- if (widget != null) {
- widget.getHandle().removeFromParent();
- }
-
- widget = new MovePlanarWidget().withPlane(plane).withDefaultHandle(.5, .25, color);
- _widgets.put(CompoundInteractWidget.MOVE_PLANAR_KEY, widget);
- _handle.attachChild(widget.getHandle());
-
- return this;
- }
-
- public CompoundInteractWidget withPlanarHandle(final MovePlane plane, final double radius, final double height,
- final ReadOnlyColorRGBA color) {
- MovePlanarWidget widget = (MovePlanarWidget) _widgets.get(CompoundInteractWidget.MOVE_PLANAR_KEY);
- if (widget != null) {
- widget.getHandle().removeFromParent();
- }
-
- widget = new MovePlanarWidget().withPlane(plane).withDefaultHandle(radius, height, color);
- _widgets.put(CompoundInteractWidget.MOVE_PLANAR_KEY, widget);
- _handle.attachChild(widget.getHandle());
-
- return this;
- }
-
- private MoveWidget verifyMoveWidget() {
- MoveWidget moveWidget = (MoveWidget) _widgets.get(CompoundInteractWidget.MOVE_KEY);
- if (moveWidget == null) {
- moveWidget = new MoveWidget();
- _widgets.put(CompoundInteractWidget.MOVE_KEY, moveWidget);
- _handle.attachChild(moveWidget.getHandle());
- }
- return moveWidget;
- }
-
- private RotateWidget verifyRotateWidget() {
- RotateWidget rotateWidget = (RotateWidget) _widgets.get(CompoundInteractWidget.ROTATE_KEY);
- if (rotateWidget == null) {
- rotateWidget = new RotateWidget();
- _widgets.put(CompoundInteractWidget.ROTATE_KEY, rotateWidget);
- _handle.attachChild(rotateWidget.getHandle());
- }
- return rotateWidget;
- }
-
- @Override
- public void targetChanged(final InteractManager manager) {
- for (final AbstractInteractWidget widget : _widgets.values()) {
- widget.targetChanged(manager);
- }
- }
-
- @Override
- public void targetDataUpdated(final InteractManager manager) {
- for (final AbstractInteractWidget widget : _widgets.values()) {
- widget.targetDataUpdated(manager);
- }
- }
-
- @Override
- public void receivedControl(final InteractManager manager) {
- for (final AbstractInteractWidget widget : _widgets.values()) {
- widget.receivedControl(manager);
- }
- }
-
- @Override
- public void render(final Renderer renderer, final InteractManager manager) {
- for (final AbstractInteractWidget widget : _widgets.values()) {
- widget.render(renderer, manager);
- }
- }
-
- @Override
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {
- // Make sure we have something to modify
- if (manager.getSpatialTarget() == null) {
- return;
- }
- // Make sure we are dragging.
- final MouseState current = inputStates.getCurrent().getMouseState();
- final MouseState previous = inputStates.getPrevious().getMouseState();
-
- if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
- if (_lastInputWidget != null) {
- _lastInputWidget.processInput(source, inputStates, inputConsumed, manager);
- _lastInputWidget = null;
- }
- return;
- }
-
- if (_lastInputWidget == null) {
- final Camera camera = source.getCanvasRenderer().getCamera();
- final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
- findPick(oldMouse, camera);
- if (_results.getNumber() <= 0) {
- return;
- }
-
- final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
- if (picked == null) {
- return;
- }
-
- for (final AbstractInteractWidget widget : _widgets.values()) {
- if (picked.hasAncestor(widget.getHandle())) {
- _lastInputWidget = widget;
- break;
- }
- }
- }
- _lastInputWidget.processInput(source, inputStates, inputConsumed, manager);
-
- // apply our filters, if any, now that we've made updates.
- applyFilters(manager);
- }
-
- @Override
- public void setInteractMatrix(final InteractMatrix matrix) {
- _interactMatrix = matrix;
- for (final AbstractInteractWidget widget : _widgets.values()) {
- widget.setInteractMatrix(matrix);
- }
- }
-
- @Override
- public InteractMatrix getInteractMatrix() {
- return _interactMatrix;
- }
-
- @Override
- public void update(final ReadOnlyTimer timer, final InteractManager manager) {
- for (final AbstractInteractWidget widget : _widgets.values()) {
- widget.update(timer, manager);
- }
- }
-
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.extension.interact.filter.UpdateFilter;
+import com.ardor3d.extension.interact.widget.MovePlanarWidget.MovePlane;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.image.Texture2D;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.util.ReadOnlyTimer;
+import com.google.common.collect.Maps;
+
+public class CompoundInteractWidget extends AbstractInteractWidget {
+ private static final String MOVE_KEY = "Move";
+ private static final String ROTATE_KEY = "Rotate";
+ private static final String MOVE_PLANAR_KEY = "MovePlanar";
+ private static final String MOVE_MULTIPLANAR_KEY = "MoveMultiPlanar";
+
+ public static double MIN_SCALE = 0.000001;
+
+ protected Map<String, AbstractInteractWidget> _widgets = Maps.newHashMap();
+
+ protected AbstractInteractWidget _lastInputWidget = null;
+
+ protected InteractMatrix _interactMatrix;
+
+ public CompoundInteractWidget() {
+ _handle = new Node("handleRoot");
+ }
+
+ @Override
+ public void addFilter(final UpdateFilter filter) {
+ for(final AbstractInteractWidget widget : _widgets.values()) {
+ widget.addFilter(filter);
+ }
+ super.addFilter(filter);
+ }
+
+ @Override
+ public void removeFilter(final UpdateFilter filter) {
+ for(final AbstractInteractWidget widget : _widgets.values()) {
+ widget.removeFilter(filter);
+ }
+ super.removeFilter(filter);
+ }
+
+ @Override
+ public void clearFilters() {
+ for(final AbstractInteractWidget widget : _widgets.values()) {
+ widget.clearFilters();
+ }
+ super.clearFilters();
+ }
+
+ public CompoundInteractWidget withMoveXAxis() {
+ verifyMoveWidget().withXAxis();
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveXAxis(final ReadOnlyColorRGBA color) {
+ verifyMoveWidget().withXAxis(color);
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveXAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
+ final double lengthGap, final double tipGap) {
+ verifyMoveWidget().withXAxis(color, scale, width, lengthGap, tipGap);
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveYAxis() {
+ verifyMoveWidget().withYAxis();
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveYAxis(final ReadOnlyColorRGBA color) {
+ verifyMoveWidget().withYAxis(color);
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveYAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
+ final double lengthGap, final double tipGap) {
+ verifyMoveWidget().withYAxis(color, scale, width, lengthGap, tipGap);
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveZAxis() {
+ verifyMoveWidget().withZAxis();
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveZAxis(final ReadOnlyColorRGBA color) {
+ verifyMoveWidget().withZAxis(color);
+ return this;
+ }
+
+ public CompoundInteractWidget withMoveZAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
+ final double lengthGap, final double tipGap) {
+ verifyMoveWidget().withZAxis(color, scale, width, lengthGap, tipGap);
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateXAxis() {
+ verifyRotateWidget().withXAxis();
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateXAxis(final ReadOnlyColorRGBA color) {
+ verifyRotateWidget().withXAxis(color);
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateXAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
+ verifyRotateWidget().withXAxis(color, scale, width);
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateYAxis() {
+ verifyRotateWidget().withYAxis();
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateYAxis(final ReadOnlyColorRGBA color) {
+ verifyRotateWidget().withYAxis(color);
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateYAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
+ verifyRotateWidget().withYAxis(color, scale, width);
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateZAxis() {
+ verifyRotateWidget().withZAxis();
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateZAxis(final ReadOnlyColorRGBA color) {
+ verifyRotateWidget().withZAxis(color);
+ return this;
+ }
+
+ public CompoundInteractWidget withRotateZAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
+ verifyRotateWidget().withZAxis(color, scale, width);
+ return this;
+ }
+
+ public CompoundInteractWidget withRingTexture(final Texture2D texture) {
+ verifyRotateWidget().setTexture(texture);
+ return this;
+ }
+
+ public CompoundInteractWidget withMultiPlanarHandle() {
+ MoveMultiPlanarWidget widget = (MoveMultiPlanarWidget) _widgets
+ .get(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY);
+ if (widget != null) {
+ widget.getHandle().removeFromParent();
+ }
+
+ widget = new MoveMultiPlanarWidget();
+ _widgets.put(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY, widget);
+ _handle.attachChild(widget.getHandle());
+
+ return this;
+ }
+
+ public CompoundInteractWidget withMultiPlanarHandle(final double extent) {
+ MoveMultiPlanarWidget widget = (MoveMultiPlanarWidget) _widgets
+ .get(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY);
+ if (widget != null) {
+ widget.getHandle().removeFromParent();
+ }
+
+ widget = new MoveMultiPlanarWidget(extent);
+ _widgets.put(CompoundInteractWidget.MOVE_MULTIPLANAR_KEY, widget);
+ _handle.attachChild(widget.getHandle());
+
+ return this;
+ }
+
+ public CompoundInteractWidget withPlanarHandle(final MovePlane plane, final ReadOnlyColorRGBA color) {
+ MovePlanarWidget widget = (MovePlanarWidget) _widgets.get(CompoundInteractWidget.MOVE_PLANAR_KEY);
+ if (widget != null) {
+ widget.getHandle().removeFromParent();
+ }
+
+ widget = new MovePlanarWidget().withPlane(plane).withDefaultHandle(.5, .25, color);
+ _widgets.put(CompoundInteractWidget.MOVE_PLANAR_KEY, widget);
+ _handle.attachChild(widget.getHandle());
+
+ return this;
+ }
+
+ public CompoundInteractWidget withPlanarHandle(final MovePlane plane, final double radius, final double height,
+ final ReadOnlyColorRGBA color) {
+ MovePlanarWidget widget = (MovePlanarWidget) _widgets.get(CompoundInteractWidget.MOVE_PLANAR_KEY);
+ if (widget != null) {
+ widget.getHandle().removeFromParent();
+ }
+
+ widget = new MovePlanarWidget().withPlane(plane).withDefaultHandle(radius, height, color);
+ _widgets.put(CompoundInteractWidget.MOVE_PLANAR_KEY, widget);
+ _handle.attachChild(widget.getHandle());
+
+ return this;
+ }
+
+ private MoveWidget verifyMoveWidget() {
+ MoveWidget moveWidget = (MoveWidget) _widgets.get(CompoundInteractWidget.MOVE_KEY);
+ if (moveWidget == null) {
+ moveWidget = new MoveWidget();
+ _widgets.put(CompoundInteractWidget.MOVE_KEY, moveWidget);
+ _handle.attachChild(moveWidget.getHandle());
+ }
+ return moveWidget;
+ }
+
+ private RotateWidget verifyRotateWidget() {
+ RotateWidget rotateWidget = (RotateWidget) _widgets.get(CompoundInteractWidget.ROTATE_KEY);
+ if (rotateWidget == null) {
+ rotateWidget = new RotateWidget();
+ _widgets.put(CompoundInteractWidget.ROTATE_KEY, rotateWidget);
+ _handle.attachChild(rotateWidget.getHandle());
+ }
+ return rotateWidget;
+ }
+
+ @Override
+ public void targetChanged(final InteractManager manager) {
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ widget.targetChanged(manager);
+ }
+ }
+
+ @Override
+ public void targetDataUpdated(final InteractManager manager) {
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ widget.targetDataUpdated(manager);
+ }
+ }
+
+ @Override
+ public void receivedControl(final InteractManager manager) {
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ widget.receivedControl(manager);
+ }
+ }
+
+ @Override
+ public void render(final Renderer renderer, final InteractManager manager) {
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ widget.render(renderer, manager);
+ }
+ }
+
+ @Override
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {
+ // Make sure we have something to modify
+ if (manager.getSpatialTarget() == null) {
+ return;
+ }
+ // Make sure we are dragging.
+ final MouseState current = inputStates.getCurrent().getMouseState();
+ final MouseState previous = inputStates.getPrevious().getMouseState();
+
+ if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
+ if (_lastInputWidget != null) {
+ _lastInputWidget.processInput(source, inputStates, inputConsumed, manager);
+ _lastInputWidget = null;
+ }
+ return;
+ }
+
+ if (_lastInputWidget == null) {
+ final Camera camera = source.getCanvasRenderer().getCamera();
+ final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
+ findPick(oldMouse, camera);
+ if (_results.getNumber() <= 0) {
+ return;
+ }
+
+ final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
+ if (picked == null) {
+ return;
+ }
+
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ if (picked.hasAncestor(widget.getHandle())) {
+ _lastInputWidget = widget;
+ break;
+ }
+ }
+ }
+ _lastInputWidget.processInput(source, inputStates, inputConsumed, manager);
+
+ // apply our filters, if any, now that we've made updates.
+ applyFilters(manager);
+ }
+
+ @Override
+ public void setInteractMatrix(final InteractMatrix matrix) {
+ _interactMatrix = matrix;
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ widget.setInteractMatrix(matrix);
+ }
+ }
+
+ @Override
+ public InteractMatrix getInteractMatrix() {
+ return _interactMatrix;
+ }
+
+ @Override
+ public void update(final ReadOnlyTimer timer, final InteractManager manager) {
+ for (final AbstractInteractWidget widget : _widgets.values()) {
+ widget.update(timer, manager);
+ }
+ }
+
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java
index 2574770..944bd9d 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractMatrix.java
@@ -1,15 +1,15 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-public enum InteractMatrix {
- World, Local;
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+public enum InteractMatrix {
+ World, Local;
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java
index be5a53f..983017a 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/InteractRing.java
@@ -1,188 +1,188 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.io.IOException;
-import java.nio.FloatBuffer;
-
-import com.ardor3d.image.Texture2D;
-import com.ardor3d.math.MathUtils;
-import com.ardor3d.renderer.IndexMode;
-import com.ardor3d.renderer.state.RenderState;
-import com.ardor3d.renderer.state.TextureState;
-import com.ardor3d.scenegraph.FloatBufferData;
-import com.ardor3d.scenegraph.Mesh;
-import com.ardor3d.util.export.InputCapsule;
-import com.ardor3d.util.export.OutputCapsule;
-import com.ardor3d.util.geom.BufferUtils;
-
-/**
- * Textured ring geometry, intended for use as a rotational handle.
- */
-public class InteractRing extends Mesh {
- protected float _innerRadius, _outerRadius;
- protected int _tessRings = 2;
- protected int _tessSteps = 32;
- protected float _texMul = 4.0f;
- protected float _concaveValue = 0;
-
- public InteractRing() {}
-
- public InteractRing(final String name, final int tessRings, final int tessSteps, final float radius,
- final float width) {
- this(name, tessRings, tessSteps, radius, width, 0);
- }
-
- public InteractRing(final String name, final int tessRings, final int tessSteps, final float radius,
- final float width, final float concaveValue) {
- super(name);
- _tessRings = tessRings;
- _tessSteps = tessSteps;
- _concaveValue = concaveValue;
- setRadius(radius, width);
- }
-
- public void setRadius(final float radius, final float width) {
- _innerRadius = radius;
- _outerRadius = radius + width;
- updateGeometry();
- }
-
- /**
- * @param vMult
- * new multiplier for v direction of texture coords (around ring)
- */
- public void setTextureMultiplier(final float vMult) {
- _texMul = vMult;
- updateGeometry();
- }
-
- public void setConcaveValue(final float value) {
- _concaveValue = value;
- updateGeometry();
- }
-
- /**
- * Convenience method for setting texture without managing TextureState.
- *
- * @param texture
- * the new texture to set on unit 0.
- */
- public void setTexture(final Texture2D texture) {
- TextureState ts = (TextureState) getLocalRenderState(RenderState.StateType.Texture);
- if (ts == null) {
- ts = new TextureState();
- ts.setEnabled(true);
- setRenderState(ts);
- }
- ts.setTexture(texture, 0);
- }
-
- /**
- *
- */
- public void updateGeometry() {
- final int numPairs = _tessSteps + 1;
- final int totalVerts = _tessRings * numPairs * 2;
-
- FloatBuffer crdBuf = getMeshData().getVertexBuffer();
- if (crdBuf == null || totalVerts != crdBuf.limit() / 3) { // allocate new buffers
- getMeshData().setVertexBuffer(BufferUtils.createFloatBuffer(totalVerts * 3));
- getMeshData().setNormalBuffer(BufferUtils.createFloatBuffer(totalVerts * 3));
- getMeshData().setTextureCoords(new FloatBufferData(BufferUtils.createFloatBuffer(totalVerts * 2), 2), 0);
- crdBuf = getMeshData().getVertexBuffer();
- }
- final FloatBuffer nrmBuf = getMeshData().getNormalBuffer();
- final FloatBufferData tc = getMeshData().getTextureCoords(0);
- final FloatBuffer txcBuf = tc.getBuffer();
- calculateVertexData(_tessRings, numPairs, totalVerts, crdBuf, nrmBuf, txcBuf);
-
- updateModelBound();
- }
-
- protected void normalize(final int i, final float[] nrm) {
- final float length = (float) MathUtils
- .sqrt(nrm[i] * nrm[i] + nrm[i + 1] * nrm[i + 1] + nrm[i + 2] * nrm[i + 2]);
- nrm[i] /= length;
- nrm[i + 1] /= length;
- nrm[i + 2] /= length;
- }
-
- protected void calculateVertexData(final int numStrips, final int numPairs, final int totalVerts,
- final FloatBuffer crdBuf, final FloatBuffer nrmBuf, final FloatBuffer txcBuf) {
- // we are generating strips
- getMeshData().setIndexMode(IndexMode.TriangleStrip);
-
- final float astep = (float) (Math.PI * 2 / _tessSteps);
- final float sstep = 1.0f / numStrips;
- final float rrange = _outerRadius - _innerRadius;
- final float rstep = rrange / numStrips;
- float xa, ya;
- float r0, r1;
- float nadd0, nadd1;
- float tc;
- final float up = 1;
- final float[] nrm = new float[6];
- crdBuf.rewind();
- nrmBuf.rewind();
- txcBuf.rewind();
- for (int s = 0; s < numStrips; s++) {
- nadd0 = _concaveValue * (s + 0 - numStrips * 0.5f) / numStrips;
- nadd1 = _concaveValue * (s + 1 - numStrips * 0.5f) / numStrips;
- for (int a = 0; a < numPairs; a++) {
- xa = (float) Math.cos(a * astep);
- ya = (float) Math.sin(a * astep);
- r0 = _innerRadius + (s + 0) * rstep;
- r1 = _innerRadius + (s + 1) * rstep;
-
- crdBuf.put(xa * r0).put(ya * r0).put(0);
- crdBuf.put(xa * r1).put(ya * r1).put(0);
-
- nrm[0] = nadd0 * xa;
- nrm[1] = nadd0 * ya;
- nrm[2] = up;
- nrm[3] = nadd1 * xa;
- nrm[4] = nadd1 * ya;
- nrm[5] = up;
- normalize(0, nrm);
- normalize(3, nrm);
- nrmBuf.put(nrm[0]).put(nrm[1]).put(nrm[2]);
- nrmBuf.put(nrm[3]).put(nrm[4]).put(nrm[5]);
-
- tc = a * _texMul / _tessSteps;
- txcBuf.put((s + 0) * sstep).put(tc);
- txcBuf.put((s + 1) * sstep).put(tc);
- }
- }
- }
-
- @Override
- public void write(final OutputCapsule capsule) throws IOException {
- super.write(capsule);
- capsule.write(_innerRadius, "innerRadius", 0f);
- capsule.write(_outerRadius, "outerRadius", 0f);
- capsule.write(_tessRings, "tessRings", 2);
- capsule.write(_tessSteps, "tessSteps", 16);
- capsule.write(_texMul, "texMul", 1f);
- capsule.write(_concaveValue, "concaveValue", 0f);
- }
-
- @Override
- public void read(final InputCapsule capsule) throws IOException {
- super.read(capsule);
- _innerRadius = capsule.readFloat("innerRadius", 0f);
- _outerRadius = capsule.readFloat("outerRadius", 0f);
- _tessRings = capsule.readInt("tessRings", 2);
- _tessSteps = capsule.readInt("tessSteps", 16);
- _texMul = capsule.readFloat("texMul", 1f);
- _concaveValue = capsule.readFloat("concaveValue", 0f);
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.io.IOException;
+import java.nio.FloatBuffer;
+
+import com.ardor3d.image.Texture2D;
+import com.ardor3d.math.MathUtils;
+import com.ardor3d.renderer.IndexMode;
+import com.ardor3d.renderer.state.RenderState;
+import com.ardor3d.renderer.state.TextureState;
+import com.ardor3d.scenegraph.FloatBufferData;
+import com.ardor3d.scenegraph.Mesh;
+import com.ardor3d.util.export.InputCapsule;
+import com.ardor3d.util.export.OutputCapsule;
+import com.ardor3d.util.geom.BufferUtils;
+
+/**
+ * Textured ring geometry, intended for use as a rotational handle.
+ */
+public class InteractRing extends Mesh {
+ protected float _innerRadius, _outerRadius;
+ protected int _tessRings = 2;
+ protected int _tessSteps = 32;
+ protected float _texMul = 4.0f;
+ protected float _concaveValue = 0;
+
+ public InteractRing() {}
+
+ public InteractRing(final String name, final int tessRings, final int tessSteps, final float radius,
+ final float width) {
+ this(name, tessRings, tessSteps, radius, width, 0);
+ }
+
+ public InteractRing(final String name, final int tessRings, final int tessSteps, final float radius,
+ final float width, final float concaveValue) {
+ super(name);
+ _tessRings = tessRings;
+ _tessSteps = tessSteps;
+ _concaveValue = concaveValue;
+ setRadius(radius, width);
+ }
+
+ public void setRadius(final float radius, final float width) {
+ _innerRadius = radius;
+ _outerRadius = radius + width;
+ updateGeometry();
+ }
+
+ /**
+ * @param vMult
+ * new multiplier for v direction of texture coords (around ring)
+ */
+ public void setTextureMultiplier(final float vMult) {
+ _texMul = vMult;
+ updateGeometry();
+ }
+
+ public void setConcaveValue(final float value) {
+ _concaveValue = value;
+ updateGeometry();
+ }
+
+ /**
+ * Convenience method for setting texture without managing TextureState.
+ *
+ * @param texture
+ * the new texture to set on unit 0.
+ */
+ public void setTexture(final Texture2D texture) {
+ TextureState ts = (TextureState) getLocalRenderState(RenderState.StateType.Texture);
+ if (ts == null) {
+ ts = new TextureState();
+ ts.setEnabled(true);
+ setRenderState(ts);
+ }
+ ts.setTexture(texture, 0);
+ }
+
+ /**
+ *
+ */
+ public void updateGeometry() {
+ final int numPairs = _tessSteps + 1;
+ final int totalVerts = _tessRings * numPairs * 2;
+
+ FloatBuffer crdBuf = getMeshData().getVertexBuffer();
+ if (crdBuf == null || totalVerts != crdBuf.limit() / 3) { // allocate new buffers
+ getMeshData().setVertexBuffer(BufferUtils.createFloatBuffer(totalVerts * 3));
+ getMeshData().setNormalBuffer(BufferUtils.createFloatBuffer(totalVerts * 3));
+ getMeshData().setTextureCoords(new FloatBufferData(BufferUtils.createFloatBuffer(totalVerts * 2), 2), 0);
+ crdBuf = getMeshData().getVertexBuffer();
+ }
+ final FloatBuffer nrmBuf = getMeshData().getNormalBuffer();
+ final FloatBufferData tc = getMeshData().getTextureCoords(0);
+ final FloatBuffer txcBuf = tc.getBuffer();
+ calculateVertexData(_tessRings, numPairs, totalVerts, crdBuf, nrmBuf, txcBuf);
+
+ updateModelBound();
+ }
+
+ protected void normalize(final int i, final float[] nrm) {
+ final float length = (float) MathUtils
+ .sqrt(nrm[i] * nrm[i] + nrm[i + 1] * nrm[i + 1] + nrm[i + 2] * nrm[i + 2]);
+ nrm[i] /= length;
+ nrm[i + 1] /= length;
+ nrm[i + 2] /= length;
+ }
+
+ protected void calculateVertexData(final int numStrips, final int numPairs, final int totalVerts,
+ final FloatBuffer crdBuf, final FloatBuffer nrmBuf, final FloatBuffer txcBuf) {
+ // we are generating strips
+ getMeshData().setIndexMode(IndexMode.TriangleStrip);
+
+ final float astep = (float) (Math.PI * 2 / _tessSteps);
+ final float sstep = 1.0f / numStrips;
+ final float rrange = _outerRadius - _innerRadius;
+ final float rstep = rrange / numStrips;
+ float xa, ya;
+ float r0, r1;
+ float nadd0, nadd1;
+ float tc;
+ final float up = 1;
+ final float[] nrm = new float[6];
+ crdBuf.rewind();
+ nrmBuf.rewind();
+ txcBuf.rewind();
+ for (int s = 0; s < numStrips; s++) {
+ nadd0 = _concaveValue * (s + 0 - numStrips * 0.5f) / numStrips;
+ nadd1 = _concaveValue * (s + 1 - numStrips * 0.5f) / numStrips;
+ for (int a = 0; a < numPairs; a++) {
+ xa = (float) Math.cos(a * astep);
+ ya = (float) Math.sin(a * astep);
+ r0 = _innerRadius + (s + 0) * rstep;
+ r1 = _innerRadius + (s + 1) * rstep;
+
+ crdBuf.put(xa * r0).put(ya * r0).put(0);
+ crdBuf.put(xa * r1).put(ya * r1).put(0);
+
+ nrm[0] = nadd0 * xa;
+ nrm[1] = nadd0 * ya;
+ nrm[2] = up;
+ nrm[3] = nadd1 * xa;
+ nrm[4] = nadd1 * ya;
+ nrm[5] = up;
+ normalize(0, nrm);
+ normalize(3, nrm);
+ nrmBuf.put(nrm[0]).put(nrm[1]).put(nrm[2]);
+ nrmBuf.put(nrm[3]).put(nrm[4]).put(nrm[5]);
+
+ tc = a * _texMul / _tessSteps;
+ txcBuf.put((s + 0) * sstep).put(tc);
+ txcBuf.put((s + 1) * sstep).put(tc);
+ }
+ }
+ }
+
+ @Override
+ public void write(final OutputCapsule capsule) throws IOException {
+ super.write(capsule);
+ capsule.write(_innerRadius, "innerRadius", 0f);
+ capsule.write(_outerRadius, "outerRadius", 0f);
+ capsule.write(_tessRings, "tessRings", 2);
+ capsule.write(_tessSteps, "tessSteps", 16);
+ capsule.write(_texMul, "texMul", 1f);
+ capsule.write(_concaveValue, "concaveValue", 0f);
+ }
+
+ @Override
+ public void read(final InputCapsule capsule) throws IOException {
+ super.read(capsule);
+ _innerRadius = capsule.readFloat("innerRadius", 0f);
+ _outerRadius = capsule.readFloat("outerRadius", 0f);
+ _tessRings = capsule.readInt("tessRings", 2);
+ _tessSteps = capsule.readInt("tessSteps", 16);
+ _texMul = capsule.readFloat("texMul", 1f);
+ _concaveValue = capsule.readFloat("concaveValue", 0f);
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java
index 7cf7dd4..019c3ed 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveMultiPlanarWidget.java
@@ -1,230 +1,230 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.nio.FloatBuffer;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.input.ButtonState;
-import com.ardor3d.input.MouseState;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.intersection.PickData;
-import com.ardor3d.intersection.PrimitiveKey;
-import com.ardor3d.math.ColorRGBA;
-import com.ardor3d.math.Matrix3;
-import com.ardor3d.math.Plane;
-import com.ardor3d.math.Transform;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.renderer.queue.RenderBucketType;
-import com.ardor3d.renderer.state.BlendState;
-import com.ardor3d.renderer.state.MaterialState;
-import com.ardor3d.renderer.state.MaterialState.ColorMaterial;
-import com.ardor3d.renderer.state.ShadingState;
-import com.ardor3d.renderer.state.ShadingState.ShadingMode;
-import com.ardor3d.renderer.state.ZBufferState;
-import com.ardor3d.renderer.state.ZBufferState.TestFunction;
-import com.ardor3d.scenegraph.Mesh;
-import com.ardor3d.scenegraph.Node;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.scenegraph.shape.Box;
-import com.ardor3d.util.geom.BufferUtils;
-
-public class MoveMultiPlanarWidget extends AbstractInteractWidget {
- public static double MIN_SCALE = 0.000001;
-
- public MoveMultiPlanarWidget() {
- this(0.5);
- }
-
- public MoveMultiPlanarWidget(final double extent) {
- _handle = new Node("moveHandle");
-
- final BlendState blend = new BlendState();
- blend.setBlendEnabled(true);
- _handle.setRenderState(blend);
-
- final ZBufferState zstate = new ZBufferState();
- zstate.setFunction(TestFunction.LessThanOrEqualTo);
- _handle.setRenderState(zstate);
-
- _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
- _handle.updateGeometricState(0);
-
- createDefaultHandle(extent);
- }
-
- protected void createDefaultHandle(final double extent) {
- final Box grip = new Box("grip", Vector3.ZERO, extent, extent, extent);
- grip.updateModelBound();
- _handle.attachChild(grip);
-
- // setup some colors, just at the corner of the primitives since we will use flat shading.
- grip.setSolidColor(ColorRGBA.WHITE);
- final FloatBuffer colors = grip.getMeshData().getColorBuffer();
- BufferUtils.setInBuffer(ColorRGBA.MAGENTA, colors, 0);
- BufferUtils.setInBuffer(ColorRGBA.CYAN, colors, 4);
- BufferUtils.setInBuffer(ColorRGBA.MAGENTA, colors, 8);
- BufferUtils.setInBuffer(ColorRGBA.CYAN, colors, 12);
- BufferUtils.setInBuffer(ColorRGBA.YELLOW, colors, 16);
- BufferUtils.setInBuffer(ColorRGBA.YELLOW, colors, 20);
-
- // set flat shading
- final ShadingState shade = new ShadingState();
- shade.setShadingMode(ShadingMode.Flat);
- grip.setRenderState(shade);
-
- // setup a material state to use the colors from the vertices.
- final MaterialState material = new MaterialState();
- material.setColorMaterial(ColorMaterial.Diffuse);
- grip.setRenderState(material);
- }
-
- @Override
- public void targetChanged(final InteractManager manager) {
- if (_dragging) {
- endDrag(manager);
- }
- final Spatial target = manager.getSpatialTarget();
- if (target != null) {
- _handle.setScale(Math.max(MoveMultiPlanarWidget.MIN_SCALE, target.getWorldBound().getRadius()
- + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
- }
- targetDataUpdated(manager);
- }
-
- @Override
- public void targetDataUpdated(final InteractManager manager) {
- final Spatial target = manager.getSpatialTarget();
- if (target == null) {
- _handle.setScale(1.0);
- _handle.setRotation(Matrix3.IDENTITY);
- } else {
- // update scale of widget using bounding radius
- target.updateGeometricState(0);
-
- // update arrow rotations from target
- if (_interactMatrix == InteractMatrix.Local) {
- _handle.setRotation(target.getWorldRotation());
- } else {
- _handle.setRotation(Matrix3.IDENTITY);
- }
- }
- }
-
- @Override
- public void render(final Renderer renderer, final InteractManager manager) {
- final Spatial spat = manager.getSpatialTarget();
- if (spat == null) {
- return;
- }
-
- _handle.setTranslation(spat.getWorldTranslation());
- _handle.updateGeometricState(0);
-
- renderer.draw(_handle);
- }
-
- @Override
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {
- // Make sure we have something to modify
- if (manager.getSpatialTarget() == null) {
- return;
- }
-
- // Make sure we are dragging.
- final MouseState current = inputStates.getCurrent().getMouseState();
- final MouseState previous = inputStates.getPrevious().getMouseState();
-
- if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
- if (_dragging) {
- endDrag(manager);
- }
- return;
- }
- // if we're already dragging, make sure we only act on drags that started with a positive pick.
- else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
- return;
- }
-
- final Camera camera = source.getCanvasRenderer().getCamera();
- final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
- // Make sure we are dragging over the handle
- if (!_dragging) {
- findPick(oldMouse, camera);
- final Vector3 lastPick = getLastPick();
- if (lastPick == null) {
- return;
- } else {
- beginDrag(manager);
- }
- }
-
- // we've established that our mouse is being held down, and started over our arrow. So consume.
- inputConsumed.set(true);
-
- // check if we've moved at all
- if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
- return;
- }
-
- // act on drag
- final PickData pickData = _results.getPickData(0);
- final Spatial picked = (Spatial) pickData.getTarget();
- if (picked instanceof Mesh && pickData.getIntersectionRecord().getNumberOfIntersections() > 0) {
- final PrimitiveKey key = pickData.getIntersectionRecord().getIntersectionPrimitive(0);
- ((Mesh) picked).getMeshData().getPrimitiveVertices(key.getPrimitiveIndex(), key.getSection(),
- new Vector3[] { _calcVec3A, _calcVec3B, _calcVec3C });
- picked.localToWorld(_calcVec3A, _calcVec3A);
- picked.localToWorld(_calcVec3B, _calcVec3B);
- picked.localToWorld(_calcVec3C, _calcVec3C);
- final Vector3 loc = getNewOffset(oldMouse, current, camera, manager);
- final Transform transform = manager.getSpatialState().getTransform();
- transform.setTranslation(loc.addLocal(transform.getTranslation()));
-
- // apply our filters, if any, now that we've made updates.
- applyFilters(manager);
- }
- }
-
- protected Vector3 getNewOffset(final Vector2 oldMouse, final MouseState current, final Camera camera,
- final InteractManager manager) {
-
- // make plane object
- final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B, _calcVec3C);
-
- // find out where we were hitting the plane before
- getPickRay(oldMouse, camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
- return _calcVec3A.zero();
- }
-
- // find out where we are hitting the plane now
- getPickRay(new Vector2(current.getX(), current.getY()), camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
- return _calcVec3A.zero();
- }
-
- // convert to target coord space
- final Node parent = manager.getSpatialTarget().getParent();
- if (parent != null) {
- parent.getWorldTransform().applyInverse(_calcVec3A);
- parent.getWorldTransform().applyInverse(_calcVec3B);
- }
-
- return _calcVec3B.subtractLocal(_calcVec3A);
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.nio.FloatBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.intersection.PickData;
+import com.ardor3d.intersection.PrimitiveKey;
+import com.ardor3d.math.ColorRGBA;
+import com.ardor3d.math.Matrix3;
+import com.ardor3d.math.Plane;
+import com.ardor3d.math.Transform;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.renderer.queue.RenderBucketType;
+import com.ardor3d.renderer.state.BlendState;
+import com.ardor3d.renderer.state.MaterialState;
+import com.ardor3d.renderer.state.MaterialState.ColorMaterial;
+import com.ardor3d.renderer.state.ShadingState;
+import com.ardor3d.renderer.state.ShadingState.ShadingMode;
+import com.ardor3d.renderer.state.ZBufferState;
+import com.ardor3d.renderer.state.ZBufferState.TestFunction;
+import com.ardor3d.scenegraph.Mesh;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.scenegraph.shape.Box;
+import com.ardor3d.util.geom.BufferUtils;
+
+public class MoveMultiPlanarWidget extends AbstractInteractWidget {
+ public static double MIN_SCALE = 0.000001;
+
+ public MoveMultiPlanarWidget() {
+ this(0.5);
+ }
+
+ public MoveMultiPlanarWidget(final double extent) {
+ _handle = new Node("moveHandle");
+
+ final BlendState blend = new BlendState();
+ blend.setBlendEnabled(true);
+ _handle.setRenderState(blend);
+
+ final ZBufferState zstate = new ZBufferState();
+ zstate.setFunction(TestFunction.LessThanOrEqualTo);
+ _handle.setRenderState(zstate);
+
+ _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
+ _handle.updateGeometricState(0);
+
+ createDefaultHandle(extent);
+ }
+
+ protected void createDefaultHandle(final double extent) {
+ final Box grip = new Box("grip", Vector3.ZERO, extent, extent, extent);
+ grip.updateModelBound();
+ _handle.attachChild(grip);
+
+ // setup some colors, just at the corner of the primitives since we will use flat shading.
+ grip.setSolidColor(ColorRGBA.WHITE);
+ final FloatBuffer colors = grip.getMeshData().getColorBuffer();
+ BufferUtils.setInBuffer(ColorRGBA.MAGENTA, colors, 0);
+ BufferUtils.setInBuffer(ColorRGBA.CYAN, colors, 4);
+ BufferUtils.setInBuffer(ColorRGBA.MAGENTA, colors, 8);
+ BufferUtils.setInBuffer(ColorRGBA.CYAN, colors, 12);
+ BufferUtils.setInBuffer(ColorRGBA.YELLOW, colors, 16);
+ BufferUtils.setInBuffer(ColorRGBA.YELLOW, colors, 20);
+
+ // set flat shading
+ final ShadingState shade = new ShadingState();
+ shade.setShadingMode(ShadingMode.Flat);
+ grip.setRenderState(shade);
+
+ // setup a material state to use the colors from the vertices.
+ final MaterialState material = new MaterialState();
+ material.setColorMaterial(ColorMaterial.Diffuse);
+ grip.setRenderState(material);
+ }
+
+ @Override
+ public void targetChanged(final InteractManager manager) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ final Spatial target = manager.getSpatialTarget();
+ if (target != null) {
+ _handle.setScale(Math.max(MoveMultiPlanarWidget.MIN_SCALE, target.getWorldBound().getRadius()
+ + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
+ }
+ targetDataUpdated(manager);
+ }
+
+ @Override
+ public void targetDataUpdated(final InteractManager manager) {
+ final Spatial target = manager.getSpatialTarget();
+ if (target == null) {
+ _handle.setScale(1.0);
+ _handle.setRotation(Matrix3.IDENTITY);
+ } else {
+ // update scale of widget using bounding radius
+ target.updateGeometricState(0);
+
+ // update arrow rotations from target
+ if (_interactMatrix == InteractMatrix.Local) {
+ _handle.setRotation(target.getWorldRotation());
+ } else {
+ _handle.setRotation(Matrix3.IDENTITY);
+ }
+ }
+ }
+
+ @Override
+ public void render(final Renderer renderer, final InteractManager manager) {
+ final Spatial spat = manager.getSpatialTarget();
+ if (spat == null) {
+ return;
+ }
+
+ _handle.setTranslation(spat.getWorldTranslation());
+ _handle.updateGeometricState(0);
+
+ renderer.draw(_handle);
+ }
+
+ @Override
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {
+ // Make sure we have something to modify
+ if (manager.getSpatialTarget() == null) {
+ return;
+ }
+
+ // Make sure we are dragging.
+ final MouseState current = inputStates.getCurrent().getMouseState();
+ final MouseState previous = inputStates.getPrevious().getMouseState();
+
+ if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ return;
+ }
+ // if we're already dragging, make sure we only act on drags that started with a positive pick.
+ else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
+ return;
+ }
+
+ final Camera camera = source.getCanvasRenderer().getCamera();
+ final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
+ // Make sure we are dragging over the handle
+ if (!_dragging) {
+ findPick(oldMouse, camera);
+ final Vector3 lastPick = getLastPick();
+ if (lastPick == null) {
+ return;
+ } else {
+ beginDrag(manager);
+ }
+ }
+
+ // we've established that our mouse is being held down, and started over our arrow. So consume.
+ inputConsumed.set(true);
+
+ // check if we've moved at all
+ if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
+ return;
+ }
+
+ // act on drag
+ final PickData pickData = _results.getPickData(0);
+ final Spatial picked = (Spatial) pickData.getTarget();
+ if (picked instanceof Mesh && pickData.getIntersectionRecord().getNumberOfIntersections() > 0) {
+ final PrimitiveKey key = pickData.getIntersectionRecord().getIntersectionPrimitive(0);
+ ((Mesh) picked).getMeshData().getPrimitiveVertices(key.getPrimitiveIndex(), key.getSection(),
+ new Vector3[] { _calcVec3A, _calcVec3B, _calcVec3C });
+ picked.localToWorld(_calcVec3A, _calcVec3A);
+ picked.localToWorld(_calcVec3B, _calcVec3B);
+ picked.localToWorld(_calcVec3C, _calcVec3C);
+ final Vector3 loc = getNewOffset(oldMouse, current, camera, manager);
+ final Transform transform = manager.getSpatialState().getTransform();
+ transform.setTranslation(loc.addLocal(transform.getTranslation()));
+
+ // apply our filters, if any, now that we've made updates.
+ applyFilters(manager);
+ }
+ }
+
+ protected Vector3 getNewOffset(final Vector2 oldMouse, final MouseState current, final Camera camera,
+ final InteractManager manager) {
+
+ // make plane object
+ final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B, _calcVec3C);
+
+ // find out where we were hitting the plane before
+ getPickRay(oldMouse, camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
+ return _calcVec3A.zero();
+ }
+
+ // find out where we are hitting the plane now
+ getPickRay(new Vector2(current.getX(), current.getY()), camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
+ return _calcVec3A.zero();
+ }
+
+ // convert to target coord space
+ final Node parent = manager.getSpatialTarget().getParent();
+ if (parent != null) {
+ parent.getWorldTransform().applyInverse(_calcVec3A);
+ parent.getWorldTransform().applyInverse(_calcVec3B);
+ }
+
+ return _calcVec3B.subtractLocal(_calcVec3A);
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java
index 7389f7f..4b551bc 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MovePlanarWidget.java
@@ -1,240 +1,240 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.input.ButtonState;
-import com.ardor3d.input.MouseState;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.math.MathUtils;
-import com.ardor3d.math.Matrix3;
-import com.ardor3d.math.Plane;
-import com.ardor3d.math.Transform;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyColorRGBA;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.renderer.queue.RenderBucketType;
-import com.ardor3d.renderer.state.BlendState;
-import com.ardor3d.renderer.state.ZBufferState;
-import com.ardor3d.renderer.state.ZBufferState.TestFunction;
-import com.ardor3d.scenegraph.Node;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.scenegraph.shape.Cylinder;
-
-public class MovePlanarWidget extends AbstractInteractWidget {
- public static double MIN_SCALE = 0.000001;
-
- protected MovePlane _plane = MovePlane.XZ;
-
- public enum MovePlane {
- XY, XZ, YZ
- }
-
- public MovePlanarWidget() {
- _handle = new Node("moveHandle");
-
- final BlendState blend = new BlendState();
- blend.setBlendEnabled(true);
- _handle.setRenderState(blend);
-
- final ZBufferState zstate = new ZBufferState();
- zstate.setFunction(TestFunction.LessThanOrEqualTo);
- _handle.setRenderState(zstate);
-
- _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
- _handle.updateGeometricState(0);
- }
-
- public MovePlanarWidget withDefaultHandle(final double radius, final double height, final ReadOnlyColorRGBA color) {
- final Cylinder handle = new Cylinder("handle", 2, 16, radius, height, true);
- handle.setDefaultColor(color);
- switch (_plane) {
- case XZ:
- handle.setRotation(new Matrix3().fromAngleNormalAxis(MathUtils.HALF_PI, Vector3.UNIT_X));
- break;
- case YZ:
- handle.setRotation(new Matrix3().fromAngleNormalAxis(MathUtils.HALF_PI, Vector3.UNIT_Y));
- break;
- default:
- // do nothing
- break;
- }
- handle.updateModelBound();
- withHandle(handle);
- return this;
- }
-
- public MovePlanarWidget withPlane(final MovePlane plane) {
- _plane = plane;
- return this;
- }
-
- public MovePlanarWidget withHandle(final Spatial handle) {
- _handle.attachChild(handle);
- return this;
- }
-
- @Override
- public void targetChanged(final InteractManager manager) {
- if (_dragging) {
- endDrag(manager);
- }
- final Spatial target = manager.getSpatialTarget();
- if (target != null) {
- _handle.setScale(Math.max(MovePlanarWidget.MIN_SCALE, target.getWorldBound().getRadius()
- + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
- }
- targetDataUpdated(manager);
- }
-
- @Override
- public void targetDataUpdated(final InteractManager manager) {
- final Spatial target = manager.getSpatialTarget();
- if (target == null) {
- _handle.setScale(1.0);
- _handle.setRotation(Matrix3.IDENTITY);
- } else {
- // update scale of widget using bounding radius
- target.updateGeometricState(0);
-
- // update arrow rotations from target
- if (_interactMatrix == InteractMatrix.Local) {
- _handle.setRotation(target.getWorldRotation());
- } else {
- _handle.setRotation(Matrix3.IDENTITY);
- }
- }
- }
-
- @Override
- public void render(final Renderer renderer, final InteractManager manager) {
- final Spatial spat = manager.getSpatialTarget();
- if (spat == null) {
- return;
- }
-
- _handle.setTranslation(spat.getWorldTranslation());
- _handle.updateGeometricState(0);
-
- renderer.draw(_handle);
- }
-
- @Override
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {
- // Make sure we have something to modify
- if (manager.getSpatialTarget() == null) {
- return;
- }
-
- // Make sure we are dragging.
- final MouseState current = inputStates.getCurrent().getMouseState();
- final MouseState previous = inputStates.getPrevious().getMouseState();
-
- if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
- if (_dragging) {
- endDrag(manager);
- }
- return;
- }
- // if we're already dragging, make sure we only act on drags that started with a positive pick.
- else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
- return;
- }
-
- final Camera camera = source.getCanvasRenderer().getCamera();
- final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
- // Make sure we are dragging over the handle
- if (!_dragging) {
- findPick(oldMouse, camera);
- final Vector3 lastPick = getLastPick();
- if (lastPick == null) {
- return;
- } else {
- beginDrag(manager);
- }
- }
-
- // we've established that our mouse is being held down, and started over our arrow. So consume.
- inputConsumed.set(true);
-
- // check if we've moved at all
- if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
- return;
- }
-
- // act on drag
- final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
- if (picked != null) {
- final Vector3 loc = getNewOffset(oldMouse, current, camera, manager);
- final Transform transform = manager.getSpatialState().getTransform();
- transform.setTranslation(loc.addLocal(transform.getTranslation()));
-
- // apply our filters, if any, now that we've made updates.
- applyFilters(manager);
- }
- }
-
- protected Vector3 getNewOffset(final Vector2 oldMouse, final MouseState current, final Camera camera,
- final InteractManager manager) {
-
- // calculate a plane
- _calcVec3A.set(_handle.getWorldTranslation());
- switch (_plane) {
- case XY:
- _calcVec3B.set(Vector3.UNIT_X);
- _calcVec3C.set(Vector3.UNIT_Y);
- break;
- case XZ:
- _calcVec3B.set(Vector3.UNIT_X);
- _calcVec3C.set(Vector3.UNIT_Z);
- break;
- case YZ:
- _calcVec3B.set(Vector3.UNIT_Y);
- _calcVec3C.set(Vector3.UNIT_Z);
- break;
- }
-
- // rotate to arrow plane
- _handle.getRotation().applyPost(_calcVec3B, _calcVec3B);
- _handle.getRotation().applyPost(_calcVec3C, _calcVec3C);
-
- // make plane object
- final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B.addLocal(_calcVec3A),
- _calcVec3C.addLocal(_calcVec3A));
-
- // find out where we were hitting the plane before
- getPickRay(oldMouse, camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
- return _calcVec3A.zero();
- }
-
- // find out where we are hitting the plane now
- getPickRay(new Vector2(current.getX(), current.getY()), camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
- return _calcVec3A.zero();
- }
-
- // convert to target coord space
- final Node parent = manager.getSpatialTarget().getParent();
- if (parent != null) {
- parent.getWorldTransform().applyInverse(_calcVec3A);
- parent.getWorldTransform().applyInverse(_calcVec3B);
- }
-
- return _calcVec3B.subtractLocal(_calcVec3A);
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.math.MathUtils;
+import com.ardor3d.math.Matrix3;
+import com.ardor3d.math.Plane;
+import com.ardor3d.math.Transform;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.renderer.queue.RenderBucketType;
+import com.ardor3d.renderer.state.BlendState;
+import com.ardor3d.renderer.state.ZBufferState;
+import com.ardor3d.renderer.state.ZBufferState.TestFunction;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.scenegraph.shape.Cylinder;
+
+public class MovePlanarWidget extends AbstractInteractWidget {
+ public static double MIN_SCALE = 0.000001;
+
+ protected MovePlane _plane = MovePlane.XZ;
+
+ public enum MovePlane {
+ XY, XZ, YZ
+ }
+
+ public MovePlanarWidget() {
+ _handle = new Node("moveHandle");
+
+ final BlendState blend = new BlendState();
+ blend.setBlendEnabled(true);
+ _handle.setRenderState(blend);
+
+ final ZBufferState zstate = new ZBufferState();
+ zstate.setFunction(TestFunction.LessThanOrEqualTo);
+ _handle.setRenderState(zstate);
+
+ _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
+ _handle.updateGeometricState(0);
+ }
+
+ public MovePlanarWidget withDefaultHandle(final double radius, final double height, final ReadOnlyColorRGBA color) {
+ final Cylinder handle = new Cylinder("handle", 2, 16, radius, height, true);
+ handle.setDefaultColor(color);
+ switch (_plane) {
+ case XZ:
+ handle.setRotation(new Matrix3().fromAngleNormalAxis(MathUtils.HALF_PI, Vector3.UNIT_X));
+ break;
+ case YZ:
+ handle.setRotation(new Matrix3().fromAngleNormalAxis(MathUtils.HALF_PI, Vector3.UNIT_Y));
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ handle.updateModelBound();
+ withHandle(handle);
+ return this;
+ }
+
+ public MovePlanarWidget withPlane(final MovePlane plane) {
+ _plane = plane;
+ return this;
+ }
+
+ public MovePlanarWidget withHandle(final Spatial handle) {
+ _handle.attachChild(handle);
+ return this;
+ }
+
+ @Override
+ public void targetChanged(final InteractManager manager) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ final Spatial target = manager.getSpatialTarget();
+ if (target != null) {
+ _handle.setScale(Math.max(MovePlanarWidget.MIN_SCALE, target.getWorldBound().getRadius()
+ + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
+ }
+ targetDataUpdated(manager);
+ }
+
+ @Override
+ public void targetDataUpdated(final InteractManager manager) {
+ final Spatial target = manager.getSpatialTarget();
+ if (target == null) {
+ _handle.setScale(1.0);
+ _handle.setRotation(Matrix3.IDENTITY);
+ } else {
+ // update scale of widget using bounding radius
+ target.updateGeometricState(0);
+
+ // update arrow rotations from target
+ if (_interactMatrix == InteractMatrix.Local) {
+ _handle.setRotation(target.getWorldRotation());
+ } else {
+ _handle.setRotation(Matrix3.IDENTITY);
+ }
+ }
+ }
+
+ @Override
+ public void render(final Renderer renderer, final InteractManager manager) {
+ final Spatial spat = manager.getSpatialTarget();
+ if (spat == null) {
+ return;
+ }
+
+ _handle.setTranslation(spat.getWorldTranslation());
+ _handle.updateGeometricState(0);
+
+ renderer.draw(_handle);
+ }
+
+ @Override
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {
+ // Make sure we have something to modify
+ if (manager.getSpatialTarget() == null) {
+ return;
+ }
+
+ // Make sure we are dragging.
+ final MouseState current = inputStates.getCurrent().getMouseState();
+ final MouseState previous = inputStates.getPrevious().getMouseState();
+
+ if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ return;
+ }
+ // if we're already dragging, make sure we only act on drags that started with a positive pick.
+ else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
+ return;
+ }
+
+ final Camera camera = source.getCanvasRenderer().getCamera();
+ final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
+ // Make sure we are dragging over the handle
+ if (!_dragging) {
+ findPick(oldMouse, camera);
+ final Vector3 lastPick = getLastPick();
+ if (lastPick == null) {
+ return;
+ } else {
+ beginDrag(manager);
+ }
+ }
+
+ // we've established that our mouse is being held down, and started over our arrow. So consume.
+ inputConsumed.set(true);
+
+ // check if we've moved at all
+ if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
+ return;
+ }
+
+ // act on drag
+ final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
+ if (picked != null) {
+ final Vector3 loc = getNewOffset(oldMouse, current, camera, manager);
+ final Transform transform = manager.getSpatialState().getTransform();
+ transform.setTranslation(loc.addLocal(transform.getTranslation()));
+
+ // apply our filters, if any, now that we've made updates.
+ applyFilters(manager);
+ }
+ }
+
+ protected Vector3 getNewOffset(final Vector2 oldMouse, final MouseState current, final Camera camera,
+ final InteractManager manager) {
+
+ // calculate a plane
+ _calcVec3A.set(_handle.getWorldTranslation());
+ switch (_plane) {
+ case XY:
+ _calcVec3B.set(Vector3.UNIT_X);
+ _calcVec3C.set(Vector3.UNIT_Y);
+ break;
+ case XZ:
+ _calcVec3B.set(Vector3.UNIT_X);
+ _calcVec3C.set(Vector3.UNIT_Z);
+ break;
+ case YZ:
+ _calcVec3B.set(Vector3.UNIT_Y);
+ _calcVec3C.set(Vector3.UNIT_Z);
+ break;
+ }
+
+ // rotate to arrow plane
+ _handle.getRotation().applyPost(_calcVec3B, _calcVec3B);
+ _handle.getRotation().applyPost(_calcVec3C, _calcVec3C);
+
+ // make plane object
+ final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B.addLocal(_calcVec3A),
+ _calcVec3C.addLocal(_calcVec3A));
+
+ // find out where we were hitting the plane before
+ getPickRay(oldMouse, camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
+ return _calcVec3A.zero();
+ }
+
+ // find out where we are hitting the plane now
+ getPickRay(new Vector2(current.getX(), current.getY()), camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
+ return _calcVec3A.zero();
+ }
+
+ // convert to target coord space
+ final Node parent = manager.getSpatialTarget().getParent();
+ if (parent != null) {
+ parent.getWorldTransform().applyInverse(_calcVec3A);
+ parent.getWorldTransform().applyInverse(_calcVec3B);
+ }
+
+ return _calcVec3B.subtractLocal(_calcVec3A);
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java
index dfdb2b1..5a6cda9 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/MoveWidget.java
@@ -1,301 +1,301 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.input.ButtonState;
-import com.ardor3d.input.MouseState;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.math.ColorRGBA;
-import com.ardor3d.math.Line3;
-import com.ardor3d.math.MathUtils;
-import com.ardor3d.math.Matrix3;
-import com.ardor3d.math.Plane;
-import com.ardor3d.math.Quaternion;
-import com.ardor3d.math.Transform;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyColorRGBA;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.renderer.queue.RenderBucketType;
-import com.ardor3d.renderer.state.BlendState;
-import com.ardor3d.renderer.state.ZBufferState;
-import com.ardor3d.renderer.state.ZBufferState.TestFunction;
-import com.ardor3d.scenegraph.Node;
-import com.ardor3d.scenegraph.Spatial;
-
-public class MoveWidget extends AbstractInteractWidget {
- public static double MIN_SCALE = 0.000001;
-
- protected InteractArrow _lastArrow = null;
-
- protected InteractArrow _xArrow = null;
- protected InteractArrow _yArrow = null;
- protected InteractArrow _zArrow = null;
-
- protected ColorRGBA _xColor = new ColorRGBA(1, 0, 0, .65f);
- protected ColorRGBA _yColor = new ColorRGBA(0, 1, 0, .65f);
- protected ColorRGBA _zColor = new ColorRGBA(0, 0, 1, .65f);
-
- protected InteractMatrix _interactMatrix = InteractMatrix.World;
-
- public MoveWidget() {
- _handle = new Node("moveHandle");
-
- final BlendState blend = new BlendState();
- blend.setBlendEnabled(true);
- _handle.setRenderState(blend);
-
- final ZBufferState zstate = new ZBufferState();
- zstate.setFunction(TestFunction.LessThanOrEqualTo);
- _handle.setRenderState(zstate);
-
- _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
- _handle.updateGeometricState(0);
- }
-
- public MoveWidget withXAxis() {
- return withXAxis(_xColor);
- }
-
- public MoveWidget withXAxis(final ReadOnlyColorRGBA color) {
- return withXAxis(color, 1.0, 0.15, 0, 0);
- }
-
- public MoveWidget withXAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
- final double lengthGap, final double tipGap) {
- if (_xArrow != null) {
- _xArrow.removeFromParent();
- }
- _xColor.set(color);
- _xArrow = new InteractArrow("xMoveArrow", scale, width, lengthGap, tipGap);
- _xArrow.setDefaultColor(color);
- final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y);
- _xArrow.setRotation(rotate);
- _handle.attachChild(_xArrow);
- return this;
- }
-
- public MoveWidget withYAxis() {
- return withYAxis(_yColor);
- }
-
- public MoveWidget withYAxis(final ReadOnlyColorRGBA color) {
- return withYAxis(color, 1.0, 0.15, 0, 0);
- }
-
- public MoveWidget withYAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
- final double lengthGap, final double tipGap) {
- if (_yArrow != null) {
- _yArrow.removeFromParent();
- }
- _yColor.set(color);
- _yArrow = new InteractArrow("yMoveArrow", scale, width, lengthGap, tipGap);
- _yArrow.setDefaultColor(color);
- final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.NEG_UNIT_X);
- _yArrow.setRotation(rotate);
- _handle.attachChild(_yArrow);
- return this;
- }
-
- public MoveWidget withZAxis() {
- return withZAxis(new ColorRGBA(0, 0, 1, .65f));
- }
-
- public MoveWidget withZAxis(final ReadOnlyColorRGBA color) {
- return withZAxis(color, 1.0, 0.15, 0, 0);
- }
-
- public MoveWidget withZAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
- final double lengthGap, final double tipGap) {
- if (_zArrow != null) {
- _zArrow.removeFromParent();
- }
- _zColor.set(color);
- _zArrow = new InteractArrow("zMoveArrow", scale, width, lengthGap, tipGap);
- _zArrow.setDefaultColor(color);
- _handle.attachChild(_zArrow);
- return this;
- }
-
- @Override
- public void targetChanged(final InteractManager manager) {
- if (_dragging) {
- endDrag(manager);
- }
- final Spatial target = manager.getSpatialTarget();
- if (target != null) {
- _handle.setScale(Math.max(MoveWidget.MIN_SCALE, target.getWorldBound().getRadius()
- + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
- }
- targetDataUpdated(manager);
- }
-
- @Override
- public void targetDataUpdated(final InteractManager manager) {
- final Spatial target = manager.getSpatialTarget();
- if (target == null) {
- _handle.setScale(1.0);
- _handle.setRotation(Matrix3.IDENTITY);
- } else {
- // update scale of widget using bounding radius
- target.updateGeometricState(0);
-
- // update arrow rotations from target
- if (_interactMatrix == InteractMatrix.Local) {
- _handle.setRotation(target.getWorldRotation());
- } else {
- _handle.setRotation(Matrix3.IDENTITY);
- }
- }
- }
-
- @Override
- public void render(final Renderer renderer, final InteractManager manager) {
- final Spatial spat = manager.getSpatialTarget();
- if (spat == null) {
- return;
- }
-
- _handle.setTranslation(spat.getWorldTranslation());
- _handle.updateGeometricState(0);
-
- renderer.draw(_handle);
- }
-
- @Override
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {
- // Make sure we have something to modify
- if (manager.getSpatialTarget() == null) {
- return;
- }
-
- // Make sure we are dragging.
- final MouseState current = inputStates.getCurrent().getMouseState();
- final MouseState previous = inputStates.getPrevious().getMouseState();
-
- if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
- if (_dragging) {
- endDrag(manager);
- }
- return;
- }
- // if we're already dragging, make sure we only act on drags that started with a positive pick.
- else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
- return;
- }
-
- final Camera camera = source.getCanvasRenderer().getCamera();
- final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
- // Make sure we are dragging over the handle
- if (!_dragging) {
- findPick(oldMouse, camera);
- final Vector3 lastPick = getLastPick();
- if (lastPick == null) {
- return;
- } else {
- beginDrag(manager);
- }
- }
-
- // we've established that our mouse is being held down, and started over our arrow. So consume.
- inputConsumed.set(true);
-
- // check if we've moved at all
- if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
- return;
- }
-
- // act on drag
- final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
- if (picked != null && picked.getParent() instanceof InteractArrow) {
- final InteractArrow arrow = (InteractArrow) picked.getParent();
- _lastArrow = arrow;
- final Vector3 loc = getNewOffset(arrow, oldMouse, current, camera, manager);
- final Transform transform = manager.getSpatialState().getTransform();
- transform.setTranslation(loc.addLocal(transform.getTranslation()));
-
- // apply our filters, if any, now that we've made updates.
- applyFilters(manager);
- }
- }
-
- protected Vector3 getNewOffset(final InteractArrow arrow, final Vector2 oldMouse, final MouseState current,
- final Camera camera, final InteractManager manager) {
-
- // calculate a plane running through the Arrow and facing the camera.
- _calcVec3A.set(_handle.getWorldTranslation());
- _calcVec3B.set(_calcVec3A).addLocal(camera.getLeft());
- _calcVec3C.set( //
- arrow == _xArrow ? Vector3.UNIT_X : //
- arrow == _yArrow ? Vector3.UNIT_Y : //
- Vector3.UNIT_Z);
-
- // rotate to arrow plane
- _handle.getRotation().applyPost(_calcVec3C, _calcVec3C);
- final Line3 arrowLine = new Line3(_calcVec3A, _calcVec3C.normalize(_calcVec3D));
-
- // make plane object
- final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B, _calcVec3C.addLocal(_calcVec3A));
-
- // find out where we were hitting the plane before
- getPickRay(oldMouse, camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
- return _calcVec3A.zero();
- }
-
- // find out where we are hitting the plane now
- getPickRay(new Vector2(current.getX(), current.getY()), camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
- return _calcVec3A.zero();
- }
-
- // Cast us to the line along our arrow
- arrowLine.distanceSquared(_calcVec3A, _calcVec3C);
- arrowLine.distanceSquared(_calcVec3B, _calcVec3D);
-
- // convert to target coord space
- final Node parent = manager.getSpatialTarget().getParent();
- if (parent != null) {
- parent.getWorldTransform().applyInverse(_calcVec3C);
- parent.getWorldTransform().applyInverse(_calcVec3D);
- }
-
- return _calcVec3D.subtractLocal(_calcVec3C);
- }
-
- @Override
- public void setInteractMatrix(final InteractMatrix matrix) {
- _interactMatrix = matrix;
- }
-
- @Override
- public InteractMatrix getInteractMatrix() {
- return _interactMatrix;
- }
-
- public InteractArrow getXArrow() {
- return _xArrow;
- }
-
- public InteractArrow getYRing() {
- return _yArrow;
- }
-
- public InteractArrow getZRing() {
- return _zArrow;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.math.ColorRGBA;
+import com.ardor3d.math.Line3;
+import com.ardor3d.math.MathUtils;
+import com.ardor3d.math.Matrix3;
+import com.ardor3d.math.Plane;
+import com.ardor3d.math.Quaternion;
+import com.ardor3d.math.Transform;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.renderer.queue.RenderBucketType;
+import com.ardor3d.renderer.state.BlendState;
+import com.ardor3d.renderer.state.ZBufferState;
+import com.ardor3d.renderer.state.ZBufferState.TestFunction;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.scenegraph.Spatial;
+
+public class MoveWidget extends AbstractInteractWidget {
+ public static double MIN_SCALE = 0.000001;
+
+ protected InteractArrow _lastArrow = null;
+
+ protected InteractArrow _xArrow = null;
+ protected InteractArrow _yArrow = null;
+ protected InteractArrow _zArrow = null;
+
+ protected ColorRGBA _xColor = new ColorRGBA(1, 0, 0, .65f);
+ protected ColorRGBA _yColor = new ColorRGBA(0, 1, 0, .65f);
+ protected ColorRGBA _zColor = new ColorRGBA(0, 0, 1, .65f);
+
+ protected InteractMatrix _interactMatrix = InteractMatrix.World;
+
+ public MoveWidget() {
+ _handle = new Node("moveHandle");
+
+ final BlendState blend = new BlendState();
+ blend.setBlendEnabled(true);
+ _handle.setRenderState(blend);
+
+ final ZBufferState zstate = new ZBufferState();
+ zstate.setFunction(TestFunction.LessThanOrEqualTo);
+ _handle.setRenderState(zstate);
+
+ _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
+ _handle.updateGeometricState(0);
+ }
+
+ public MoveWidget withXAxis() {
+ return withXAxis(_xColor);
+ }
+
+ public MoveWidget withXAxis(final ReadOnlyColorRGBA color) {
+ return withXAxis(color, 1.0, 0.15, 0, 0);
+ }
+
+ public MoveWidget withXAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
+ final double lengthGap, final double tipGap) {
+ if (_xArrow != null) {
+ _xArrow.removeFromParent();
+ }
+ _xColor.set(color);
+ _xArrow = new InteractArrow("xMoveArrow", scale, width, lengthGap, tipGap);
+ _xArrow.setDefaultColor(color);
+ final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y);
+ _xArrow.setRotation(rotate);
+ _handle.attachChild(_xArrow);
+ return this;
+ }
+
+ public MoveWidget withYAxis() {
+ return withYAxis(_yColor);
+ }
+
+ public MoveWidget withYAxis(final ReadOnlyColorRGBA color) {
+ return withYAxis(color, 1.0, 0.15, 0, 0);
+ }
+
+ public MoveWidget withYAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
+ final double lengthGap, final double tipGap) {
+ if (_yArrow != null) {
+ _yArrow.removeFromParent();
+ }
+ _yColor.set(color);
+ _yArrow = new InteractArrow("yMoveArrow", scale, width, lengthGap, tipGap);
+ _yArrow.setDefaultColor(color);
+ final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.NEG_UNIT_X);
+ _yArrow.setRotation(rotate);
+ _handle.attachChild(_yArrow);
+ return this;
+ }
+
+ public MoveWidget withZAxis() {
+ return withZAxis(new ColorRGBA(0, 0, 1, .65f));
+ }
+
+ public MoveWidget withZAxis(final ReadOnlyColorRGBA color) {
+ return withZAxis(color, 1.0, 0.15, 0, 0);
+ }
+
+ public MoveWidget withZAxis(final ReadOnlyColorRGBA color, final double scale, final double width,
+ final double lengthGap, final double tipGap) {
+ if (_zArrow != null) {
+ _zArrow.removeFromParent();
+ }
+ _zColor.set(color);
+ _zArrow = new InteractArrow("zMoveArrow", scale, width, lengthGap, tipGap);
+ _zArrow.setDefaultColor(color);
+ _handle.attachChild(_zArrow);
+ return this;
+ }
+
+ @Override
+ public void targetChanged(final InteractManager manager) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ final Spatial target = manager.getSpatialTarget();
+ if (target != null) {
+ _handle.setScale(Math.max(MoveWidget.MIN_SCALE, target.getWorldBound().getRadius()
+ + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
+ }
+ targetDataUpdated(manager);
+ }
+
+ @Override
+ public void targetDataUpdated(final InteractManager manager) {
+ final Spatial target = manager.getSpatialTarget();
+ if (target == null) {
+ _handle.setScale(1.0);
+ _handle.setRotation(Matrix3.IDENTITY);
+ } else {
+ // update scale of widget using bounding radius
+ target.updateGeometricState(0);
+
+ // update arrow rotations from target
+ if (_interactMatrix == InteractMatrix.Local) {
+ _handle.setRotation(target.getWorldRotation());
+ } else {
+ _handle.setRotation(Matrix3.IDENTITY);
+ }
+ }
+ }
+
+ @Override
+ public void render(final Renderer renderer, final InteractManager manager) {
+ final Spatial spat = manager.getSpatialTarget();
+ if (spat == null) {
+ return;
+ }
+
+ _handle.setTranslation(spat.getWorldTranslation());
+ _handle.updateGeometricState(0);
+
+ renderer.draw(_handle);
+ }
+
+ @Override
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {
+ // Make sure we have something to modify
+ if (manager.getSpatialTarget() == null) {
+ return;
+ }
+
+ // Make sure we are dragging.
+ final MouseState current = inputStates.getCurrent().getMouseState();
+ final MouseState previous = inputStates.getPrevious().getMouseState();
+
+ if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ return;
+ }
+ // if we're already dragging, make sure we only act on drags that started with a positive pick.
+ else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
+ return;
+ }
+
+ final Camera camera = source.getCanvasRenderer().getCamera();
+ final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
+ // Make sure we are dragging over the handle
+ if (!_dragging) {
+ findPick(oldMouse, camera);
+ final Vector3 lastPick = getLastPick();
+ if (lastPick == null) {
+ return;
+ } else {
+ beginDrag(manager);
+ }
+ }
+
+ // we've established that our mouse is being held down, and started over our arrow. So consume.
+ inputConsumed.set(true);
+
+ // check if we've moved at all
+ if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
+ return;
+ }
+
+ // act on drag
+ final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
+ if (picked != null && picked.getParent() instanceof InteractArrow) {
+ final InteractArrow arrow = (InteractArrow) picked.getParent();
+ _lastArrow = arrow;
+ final Vector3 loc = getNewOffset(arrow, oldMouse, current, camera, manager);
+ final Transform transform = manager.getSpatialState().getTransform();
+ transform.setTranslation(loc.addLocal(transform.getTranslation()));
+
+ // apply our filters, if any, now that we've made updates.
+ applyFilters(manager);
+ }
+ }
+
+ protected Vector3 getNewOffset(final InteractArrow arrow, final Vector2 oldMouse, final MouseState current,
+ final Camera camera, final InteractManager manager) {
+
+ // calculate a plane running through the Arrow and facing the camera.
+ _calcVec3A.set(_handle.getWorldTranslation());
+ _calcVec3B.set(_calcVec3A).addLocal(camera.getLeft());
+ _calcVec3C.set( //
+ arrow == _xArrow ? Vector3.UNIT_X : //
+ arrow == _yArrow ? Vector3.UNIT_Y : //
+ Vector3.UNIT_Z);
+
+ // rotate to arrow plane
+ _handle.getRotation().applyPost(_calcVec3C, _calcVec3C);
+ final Line3 arrowLine = new Line3(_calcVec3A, _calcVec3C.normalize(_calcVec3D));
+
+ // make plane object
+ final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B, _calcVec3C.addLocal(_calcVec3A));
+
+ // find out where we were hitting the plane before
+ getPickRay(oldMouse, camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
+ return _calcVec3A.zero();
+ }
+
+ // find out where we are hitting the plane now
+ getPickRay(new Vector2(current.getX(), current.getY()), camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
+ return _calcVec3A.zero();
+ }
+
+ // Cast us to the line along our arrow
+ arrowLine.distanceSquared(_calcVec3A, _calcVec3C);
+ arrowLine.distanceSquared(_calcVec3B, _calcVec3D);
+
+ // convert to target coord space
+ final Node parent = manager.getSpatialTarget().getParent();
+ if (parent != null) {
+ parent.getWorldTransform().applyInverse(_calcVec3C);
+ parent.getWorldTransform().applyInverse(_calcVec3D);
+ }
+
+ return _calcVec3D.subtractLocal(_calcVec3C);
+ }
+
+ @Override
+ public void setInteractMatrix(final InteractMatrix matrix) {
+ _interactMatrix = matrix;
+ }
+
+ @Override
+ public InteractMatrix getInteractMatrix() {
+ return _interactMatrix;
+ }
+
+ public InteractArrow getXArrow() {
+ return _xArrow;
+ }
+
+ public InteractArrow getYRing() {
+ return _yArrow;
+ }
+
+ public InteractArrow getZRing() {
+ return _zArrow;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java
index 50c3a1a..9040208 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/RotateWidget.java
@@ -1,348 +1,348 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.image.Texture2D;
-import com.ardor3d.input.ButtonState;
-import com.ardor3d.input.MouseState;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.math.ColorRGBA;
-import com.ardor3d.math.MathUtils;
-import com.ardor3d.math.Matrix3;
-import com.ardor3d.math.Plane;
-import com.ardor3d.math.Quaternion;
-import com.ardor3d.math.Transform;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyColorRGBA;
-import com.ardor3d.math.type.ReadOnlyMatrix3;
-import com.ardor3d.math.type.ReadOnlyQuaternion;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.renderer.queue.RenderBucketType;
-import com.ardor3d.renderer.state.BlendState;
-import com.ardor3d.renderer.state.ZBufferState;
-import com.ardor3d.renderer.state.ZBufferState.TestFunction;
-import com.ardor3d.scenegraph.Node;
-import com.ardor3d.scenegraph.Spatial;
-
-public class RotateWidget extends AbstractInteractWidget {
- public static double MIN_SCALE = 0.000001;
-
- protected Matrix3 _calcMat3 = new Matrix3();
- protected Matrix3 _rotateStore = new Matrix3();
-
- protected InteractRing _lastRing = null;
-
- protected InteractRing _xRing = null;
- protected InteractRing _yRing = null;
- protected InteractRing _zRing = null;
-
- public RotateWidget() {
- _handle = new Node("rotationHandle");
-
- final BlendState blend = new BlendState();
- blend.setBlendEnabled(true);
- _handle.setRenderState(blend);
-
- final ZBufferState zstate = new ZBufferState();
- zstate.setFunction(TestFunction.LessThanOrEqualTo);
- _handle.setRenderState(zstate);
-
- _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
- _handle.updateGeometricState(0);
- }
-
- /**
- * Call this after creating the rings you want to use.
- *
- * @param texture
- * @return
- */
- public void setTexture(final Texture2D texture) {
- if (_xRing != null) {
- _xRing.setTexture(texture);
- }
- if (_yRing != null) {
- _yRing.setTexture(texture);
- }
- if (_zRing != null) {
- _zRing.setTexture(texture);
- }
- }
-
- public RotateWidget withXAxis() {
- return withXAxis(new ColorRGBA(1, 0, 0, .65f));
- }
-
- public RotateWidget withXAxis(final ReadOnlyColorRGBA color) {
- return withXAxis(color, 1.0f, 0.15f);
- }
-
- public RotateWidget withXAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
- if (_xRing != null) {
- _xRing.removeFromParent();
- }
- _xRing = new InteractRing("xRotRing", 4, 32, scale, width);
- _xRing.setDefaultColor(color);
- final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y);
- _xRing.getMeshData().rotatePoints(rotate);
- _xRing.getMeshData().rotateNormals(rotate);
- _handle.attachChild(_xRing);
- return this;
- }
-
- public RotateWidget withYAxis() {
- return withYAxis(new ColorRGBA(0, 1, 0, .65f));
- }
-
- public RotateWidget withYAxis(final ReadOnlyColorRGBA color) {
- return withYAxis(color, 1.0f, 0.15f);
- }
-
- public RotateWidget withYAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
- if (_yRing != null) {
- _yRing.removeFromParent();
- }
- _yRing = new InteractRing("yRotRing", 4, 32, scale, width);
- _yRing.setDefaultColor(color);
- final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.NEG_UNIT_X);
- _yRing.getMeshData().rotatePoints(rotate);
- _yRing.getMeshData().rotateNormals(rotate);
- _handle.attachChild(_yRing);
- return this;
- }
-
- public RotateWidget withZAxis() {
- return withZAxis(new ColorRGBA(0, 0, 1, .65f));
- }
-
- public RotateWidget withZAxis(final ReadOnlyColorRGBA color) {
- return withZAxis(color, 1.0f, 0.15f);
- }
-
- public RotateWidget withZAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
- if (_zRing != null) {
- _zRing.removeFromParent();
- }
- _zRing = new InteractRing("zRotRing", 4, 32, scale, width);
- _zRing.setDefaultColor(color);
- _handle.attachChild(_zRing);
- return this;
- }
-
- @Override
- public void targetChanged(final InteractManager manager) {
- if (_dragging) {
- endDrag(manager);
- }
- final Spatial target = manager.getSpatialTarget();
- if (target != null) {
- _handle.setScale(Math.max(RotateWidget.MIN_SCALE, target.getWorldBound().getRadius()
- + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
- }
- targetDataUpdated(manager);
- }
-
- protected void setRingRotations(final ReadOnlyMatrix3 rot) {
- if (_xRing != null) {
- _xRing.setRotation(rot);
- }
- if (_yRing != null) {
- _yRing.setRotation(rot);
- }
- if (_zRing != null) {
- _zRing.setRotation(rot);
- }
- }
-
- @Override
- public void targetDataUpdated(final InteractManager manager) {
- final Spatial target = manager.getSpatialTarget();
- if (target == null) {
- _handle.setScale(1.0);
- setRingRotations(Matrix3.IDENTITY);
- } else {
- // update scale of widget using bounding radius
- target.updateGeometricState(0);
-
- // update ring rotations from target
- if (_interactMatrix == InteractMatrix.Local) {
- setRingRotations(target.getWorldRotation());
- } else {
- setRingRotations(Matrix3.IDENTITY);
- if (_lastRing != null) {
- _lastRing.setRotation(_rotateStore);
- }
- }
- }
- }
-
- @Override
- public void render(final Renderer renderer, final InteractManager manager) {
- final Spatial spat = manager.getSpatialTarget();
- if (spat == null) {
- return;
- }
-
- _handle.setTranslation(spat.getWorldTranslation());
- _handle.updateGeometricState(0);
-
- renderer.draw(_handle);
- }
-
- @Override
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {
- // Make sure we have something to modify
- if (manager.getSpatialTarget() == null) {
- return;
- }
-
- // Make sure we are dragging.
- final MouseState current = inputStates.getCurrent().getMouseState();
- final MouseState previous = inputStates.getPrevious().getMouseState();
-
- if (current.getButtonsReleasedSince(previous).contains(_dragButton)) {
- _rotateStore.setIdentity();
- if (_interactMatrix != InteractMatrix.Local) {
- setRingRotations(Matrix3.IDENTITY);
- }
- }
-
- if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
- if (_dragging) {
- endDrag(manager);
- }
- return;
- }
- // if we're already dragging, make sure we only act on drags that started with a positive pick.
- else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
- return;
- }
-
- final Camera camera = source.getCanvasRenderer().getCamera();
- final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
- // Make sure we are dragging over the handle
- if (!_dragging) {
- findPick(oldMouse, camera);
- final Vector3 lastPick = getLastPick();
- if (lastPick == null) {
- return;
- } else {
- beginDrag(manager);
- }
- }
-
- // we've established that our mouse is being held down, and started over our arrow. So consume.
- inputConsumed.set(true);
-
- // check if we've moved at all
- if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
- return;
- }
-
- // act on drag
- final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
- if (picked instanceof InteractRing) {
- final InteractRing ring = (InteractRing) picked;
- _lastRing = ring;
- final ReadOnlyQuaternion rot = getNewAxisRotation(ring, oldMouse, current, camera, manager);
- final Transform transform = manager.getSpatialState().getTransform();
- rot.toRotationMatrix(_calcMat3).multiply(transform.getMatrix(), _calcMat3);
- transform.setRotation(_calcMat3);
-
- // apply our filters, if any, now that we've made updates.
- applyFilters(manager);
- }
- }
-
- protected ReadOnlyQuaternion getNewAxisRotation(final InteractRing ring, final Vector2 oldMouse,
- final MouseState current, final Camera camera, final InteractManager manager) {
- // calculate a plane running through the ring we picked
- _calcVec3A.set(_handle.getWorldTranslation());
- if (ring == _zRing || ring == _yRing) {
- _calcVec3B.set(Vector3.UNIT_X);
- } else {
- _calcVec3B.set(Vector3.UNIT_Z);
- }
-
- if (ring == _zRing || ring == _xRing) {
- _calcVec3C.set(Vector3.UNIT_Y);
- } else {
- _calcVec3C.set(Vector3.UNIT_Z);
- }
-
- // rotate to ring plane
- ring.getRotation().applyPost(_calcVec3B, _calcVec3B);
- ring.getRotation().applyPost(_calcVec3C, _calcVec3C);
-
- // make plane object
- final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B.addLocal(_calcVec3A),
- _calcVec3C.addLocal(_calcVec3A));
-
- // find out where we were hitting the plane before
- getPickRay(oldMouse, camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
- return Quaternion.IDENTITY;
- }
-
- // find out where we are hitting the plane now
- getPickRay(new Vector2(current.getX(), current.getY()), camera);
- if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
- return Quaternion.IDENTITY;
- }
-
- // convert to vectors
- _calcVec3A.subtractLocal(_handle.getWorldTranslation());
- _calcVec3B.subtractLocal(_handle.getWorldTranslation());
-
- // apply to our interact matrix if used
- if (_interactMatrix == InteractMatrix.World) {
- _rotateStore.multiplyLocal(new Quaternion().fromVectorToVector(_calcVec3A, _calcVec3B).toRotationMatrix(
- _calcMat3));
- }
-
- // convert to target coord space
- final Node parent = manager.getSpatialTarget().getParent();
- if (parent != null) {
- parent.getWorldTransform().applyInverseVector(_calcVec3A);
- parent.getWorldTransform().applyInverseVector(_calcVec3B);
- }
-
- // return a rotation to take us to the new rotation
- return new Quaternion().fromVectorToVector(_calcVec3A, _calcVec3B);
- }
-
- @Override
- public void setInteractMatrix(final InteractMatrix matrix) {
- if (_interactMatrix != matrix) {
- _lastRing = null;
- _interactMatrix = matrix;
- }
- }
-
- public InteractRing getXRing() {
- return _xRing;
- }
-
- public InteractRing getYRing() {
- return _yRing;
- }
-
- public InteractRing getZRing() {
- return _zRing;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.image.Texture2D;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.math.ColorRGBA;
+import com.ardor3d.math.MathUtils;
+import com.ardor3d.math.Matrix3;
+import com.ardor3d.math.Plane;
+import com.ardor3d.math.Quaternion;
+import com.ardor3d.math.Transform;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+import com.ardor3d.math.type.ReadOnlyMatrix3;
+import com.ardor3d.math.type.ReadOnlyQuaternion;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.renderer.queue.RenderBucketType;
+import com.ardor3d.renderer.state.BlendState;
+import com.ardor3d.renderer.state.ZBufferState;
+import com.ardor3d.renderer.state.ZBufferState.TestFunction;
+import com.ardor3d.scenegraph.Node;
+import com.ardor3d.scenegraph.Spatial;
+
+public class RotateWidget extends AbstractInteractWidget {
+ public static double MIN_SCALE = 0.000001;
+
+ protected Matrix3 _calcMat3 = new Matrix3();
+ protected Matrix3 _rotateStore = new Matrix3();
+
+ protected InteractRing _lastRing = null;
+
+ protected InteractRing _xRing = null;
+ protected InteractRing _yRing = null;
+ protected InteractRing _zRing = null;
+
+ public RotateWidget() {
+ _handle = new Node("rotationHandle");
+
+ final BlendState blend = new BlendState();
+ blend.setBlendEnabled(true);
+ _handle.setRenderState(blend);
+
+ final ZBufferState zstate = new ZBufferState();
+ zstate.setFunction(TestFunction.LessThanOrEqualTo);
+ _handle.setRenderState(zstate);
+
+ _handle.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
+ _handle.updateGeometricState(0);
+ }
+
+ /**
+ * Call this after creating the rings you want to use.
+ *
+ * @param texture
+ * @return
+ */
+ public void setTexture(final Texture2D texture) {
+ if (_xRing != null) {
+ _xRing.setTexture(texture);
+ }
+ if (_yRing != null) {
+ _yRing.setTexture(texture);
+ }
+ if (_zRing != null) {
+ _zRing.setTexture(texture);
+ }
+ }
+
+ public RotateWidget withXAxis() {
+ return withXAxis(new ColorRGBA(1, 0, 0, .65f));
+ }
+
+ public RotateWidget withXAxis(final ReadOnlyColorRGBA color) {
+ return withXAxis(color, 1.0f, 0.15f);
+ }
+
+ public RotateWidget withXAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
+ if (_xRing != null) {
+ _xRing.removeFromParent();
+ }
+ _xRing = new InteractRing("xRotRing", 4, 32, scale, width);
+ _xRing.setDefaultColor(color);
+ final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y);
+ _xRing.getMeshData().rotatePoints(rotate);
+ _xRing.getMeshData().rotateNormals(rotate);
+ _handle.attachChild(_xRing);
+ return this;
+ }
+
+ public RotateWidget withYAxis() {
+ return withYAxis(new ColorRGBA(0, 1, 0, .65f));
+ }
+
+ public RotateWidget withYAxis(final ReadOnlyColorRGBA color) {
+ return withYAxis(color, 1.0f, 0.15f);
+ }
+
+ public RotateWidget withYAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
+ if (_yRing != null) {
+ _yRing.removeFromParent();
+ }
+ _yRing = new InteractRing("yRotRing", 4, 32, scale, width);
+ _yRing.setDefaultColor(color);
+ final Quaternion rotate = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.NEG_UNIT_X);
+ _yRing.getMeshData().rotatePoints(rotate);
+ _yRing.getMeshData().rotateNormals(rotate);
+ _handle.attachChild(_yRing);
+ return this;
+ }
+
+ public RotateWidget withZAxis() {
+ return withZAxis(new ColorRGBA(0, 0, 1, .65f));
+ }
+
+ public RotateWidget withZAxis(final ReadOnlyColorRGBA color) {
+ return withZAxis(color, 1.0f, 0.15f);
+ }
+
+ public RotateWidget withZAxis(final ReadOnlyColorRGBA color, final float scale, final float width) {
+ if (_zRing != null) {
+ _zRing.removeFromParent();
+ }
+ _zRing = new InteractRing("zRotRing", 4, 32, scale, width);
+ _zRing.setDefaultColor(color);
+ _handle.attachChild(_zRing);
+ return this;
+ }
+
+ @Override
+ public void targetChanged(final InteractManager manager) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ final Spatial target = manager.getSpatialTarget();
+ if (target != null) {
+ _handle.setScale(Math.max(RotateWidget.MIN_SCALE, target.getWorldBound().getRadius()
+ + target.getWorldTranslation().subtract(target.getWorldBound().getCenter(), _calcVec3A).length()));
+ }
+ targetDataUpdated(manager);
+ }
+
+ protected void setRingRotations(final ReadOnlyMatrix3 rot) {
+ if (_xRing != null) {
+ _xRing.setRotation(rot);
+ }
+ if (_yRing != null) {
+ _yRing.setRotation(rot);
+ }
+ if (_zRing != null) {
+ _zRing.setRotation(rot);
+ }
+ }
+
+ @Override
+ public void targetDataUpdated(final InteractManager manager) {
+ final Spatial target = manager.getSpatialTarget();
+ if (target == null) {
+ _handle.setScale(1.0);
+ setRingRotations(Matrix3.IDENTITY);
+ } else {
+ // update scale of widget using bounding radius
+ target.updateGeometricState(0);
+
+ // update ring rotations from target
+ if (_interactMatrix == InteractMatrix.Local) {
+ setRingRotations(target.getWorldRotation());
+ } else {
+ setRingRotations(Matrix3.IDENTITY);
+ if (_lastRing != null) {
+ _lastRing.setRotation(_rotateStore);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void render(final Renderer renderer, final InteractManager manager) {
+ final Spatial spat = manager.getSpatialTarget();
+ if (spat == null) {
+ return;
+ }
+
+ _handle.setTranslation(spat.getWorldTranslation());
+ _handle.updateGeometricState(0);
+
+ renderer.draw(_handle);
+ }
+
+ @Override
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {
+ // Make sure we have something to modify
+ if (manager.getSpatialTarget() == null) {
+ return;
+ }
+
+ // Make sure we are dragging.
+ final MouseState current = inputStates.getCurrent().getMouseState();
+ final MouseState previous = inputStates.getPrevious().getMouseState();
+
+ if (current.getButtonsReleasedSince(previous).contains(_dragButton)) {
+ _rotateStore.setIdentity();
+ if (_interactMatrix != InteractMatrix.Local) {
+ setRingRotations(Matrix3.IDENTITY);
+ }
+ }
+
+ if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ return;
+ }
+ // if we're already dragging, make sure we only act on drags that started with a positive pick.
+ else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
+ return;
+ }
+
+ final Camera camera = source.getCanvasRenderer().getCamera();
+ final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
+ // Make sure we are dragging over the handle
+ if (!_dragging) {
+ findPick(oldMouse, camera);
+ final Vector3 lastPick = getLastPick();
+ if (lastPick == null) {
+ return;
+ } else {
+ beginDrag(manager);
+ }
+ }
+
+ // we've established that our mouse is being held down, and started over our arrow. So consume.
+ inputConsumed.set(true);
+
+ // check if we've moved at all
+ if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
+ return;
+ }
+
+ // act on drag
+ final Spatial picked = (Spatial) _results.getPickData(0).getTarget();
+ if (picked instanceof InteractRing) {
+ final InteractRing ring = (InteractRing) picked;
+ _lastRing = ring;
+ final ReadOnlyQuaternion rot = getNewAxisRotation(ring, oldMouse, current, camera, manager);
+ final Transform transform = manager.getSpatialState().getTransform();
+ rot.toRotationMatrix(_calcMat3).multiply(transform.getMatrix(), _calcMat3);
+ transform.setRotation(_calcMat3);
+
+ // apply our filters, if any, now that we've made updates.
+ applyFilters(manager);
+ }
+ }
+
+ protected ReadOnlyQuaternion getNewAxisRotation(final InteractRing ring, final Vector2 oldMouse,
+ final MouseState current, final Camera camera, final InteractManager manager) {
+ // calculate a plane running through the ring we picked
+ _calcVec3A.set(_handle.getWorldTranslation());
+ if (ring == _zRing || ring == _yRing) {
+ _calcVec3B.set(Vector3.UNIT_X);
+ } else {
+ _calcVec3B.set(Vector3.UNIT_Z);
+ }
+
+ if (ring == _zRing || ring == _xRing) {
+ _calcVec3C.set(Vector3.UNIT_Y);
+ } else {
+ _calcVec3C.set(Vector3.UNIT_Z);
+ }
+
+ // rotate to ring plane
+ ring.getRotation().applyPost(_calcVec3B, _calcVec3B);
+ ring.getRotation().applyPost(_calcVec3C, _calcVec3C);
+
+ // make plane object
+ final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B.addLocal(_calcVec3A),
+ _calcVec3C.addLocal(_calcVec3A));
+
+ // find out where we were hitting the plane before
+ getPickRay(oldMouse, camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3A)) {
+ return Quaternion.IDENTITY;
+ }
+
+ // find out where we are hitting the plane now
+ getPickRay(new Vector2(current.getX(), current.getY()), camera);
+ if (!_calcRay.intersectsPlane(pickPlane, _calcVec3B)) {
+ return Quaternion.IDENTITY;
+ }
+
+ // convert to vectors
+ _calcVec3A.subtractLocal(_handle.getWorldTranslation());
+ _calcVec3B.subtractLocal(_handle.getWorldTranslation());
+
+ // apply to our interact matrix if used
+ if (_interactMatrix == InteractMatrix.World) {
+ _rotateStore.multiplyLocal(new Quaternion().fromVectorToVector(_calcVec3A, _calcVec3B).toRotationMatrix(
+ _calcMat3));
+ }
+
+ // convert to target coord space
+ final Node parent = manager.getSpatialTarget().getParent();
+ if (parent != null) {
+ parent.getWorldTransform().applyInverseVector(_calcVec3A);
+ parent.getWorldTransform().applyInverseVector(_calcVec3B);
+ }
+
+ // return a rotation to take us to the new rotation
+ return new Quaternion().fromVectorToVector(_calcVec3A, _calcVec3B);
+ }
+
+ @Override
+ public void setInteractMatrix(final InteractMatrix matrix) {
+ if (_interactMatrix != matrix) {
+ _lastRing = null;
+ _interactMatrix = matrix;
+ }
+ }
+
+ public InteractRing getXRing() {
+ return _xRing;
+ }
+
+ public InteractRing getYRing() {
+ return _yRing;
+ }
+
+ public InteractRing getZRing() {
+ return _zRing;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java
index e03dda1..617c603 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/interact/widget/SimpleScaleWidget.java
@@ -1,184 +1,184 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.interact.widget;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.ardor3d.extension.interact.InteractManager;
-import com.ardor3d.framework.Canvas;
-import com.ardor3d.input.ButtonState;
-import com.ardor3d.input.MouseState;
-import com.ardor3d.input.logical.TwoInputStates;
-import com.ardor3d.math.ColorRGBA;
-import com.ardor3d.math.Plane;
-import com.ardor3d.math.Quaternion;
-import com.ardor3d.math.Vector2;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyColorRGBA;
-import com.ardor3d.math.type.ReadOnlyVector3;
-import com.ardor3d.renderer.Camera;
-import com.ardor3d.renderer.Renderer;
-import com.ardor3d.renderer.queue.RenderBucketType;
-import com.ardor3d.renderer.state.BlendState;
-import com.ardor3d.renderer.state.ZBufferState;
-import com.ardor3d.renderer.state.ZBufferState.TestFunction;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.scenegraph.shape.Arrow;
-
-public class SimpleScaleWidget extends AbstractInteractWidget {
- public static double MIN_SCALE = 0.000001;
-
- protected ReadOnlyVector3 _arrowDirection;
-
- public SimpleScaleWidget() {}
-
- public SimpleScaleWidget withArrow(final ReadOnlyVector3 arrowDirection) {
- return withArrow(arrowDirection, new ColorRGBA(1.0f, 0.0f, 0.0f, 0.4f), 0, 0);
- }
-
- public SimpleScaleWidget withArrow(final ReadOnlyVector3 arrowDirection, final ReadOnlyColorRGBA color) {
- return withArrow(arrowDirection, color, 0, 0);
- }
-
- public SimpleScaleWidget withArrow(final ReadOnlyVector3 arrowDirection, final ReadOnlyColorRGBA color,
- final double lengthGap, final double tipGap) {
- _arrowDirection = new Vector3(arrowDirection);
- _handle = new InteractArrow("scaleHandle", 1.0, 0.125, lengthGap, tipGap);
- if (!_arrowDirection.equals(Vector3.UNIT_Z)) {
- _handle.setRotation(new Quaternion().fromVectorToVector(Vector3.UNIT_Z, _arrowDirection));
- }
-
- final BlendState blend = new BlendState();
- blend.setBlendEnabled(true);
- _handle.setRenderState(blend);
-
- ((Arrow) _handle).setDefaultColor(color);
-
- final ZBufferState zstate = new ZBufferState();
- zstate.setWritable(false);
- zstate.setFunction(TestFunction.Always);
- _handle.setRenderState(zstate);
-
- _handle.getSceneHints().setRenderBucketType(RenderBucketType.PostBucket);
- _handle.updateGeometricState(0);
- return this;
- }
-
- @Override
- public void targetChanged(final InteractManager manager) {
- if (_dragging) {
- endDrag(manager);
- }
- targetDataUpdated(manager);
- }
-
- @Override
- public void targetDataUpdated(final InteractManager manager) {
- final Spatial target = manager.getSpatialTarget();
- if (target == null) {
- _handle.setScale(1.0);
- } else {
- _handle.setScale(Math.max(SimpleScaleWidget.MIN_SCALE, target.getWorldBound().getRadius()));
- }
- }
-
- @Override
- public void render(final Renderer renderer, final InteractManager manager) {
- final Spatial spat = manager.getSpatialTarget();
- if (spat == null) {
- return;
- }
-
- _handle.setTranslation(spat.getWorldTranslation());
- _handle.updateGeometricState(0);
-
- renderer.draw(_handle);
- }
-
- @Override
- public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
- final InteractManager manager) {
- // Make sure we have something to modify
- if (manager.getSpatialTarget() == null) {
- return;
- }
-
- // Make sure we are dragging.
- final MouseState current = inputStates.getCurrent().getMouseState();
- final MouseState previous = inputStates.getPrevious().getMouseState();
- if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
- if (_dragging) {
- endDrag(manager);
- }
- return;
- }
- // if we're already dragging, make sure we only act on drags that started with a positive pick.
- else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
- return;
- }
-
- final Camera camera = source.getCanvasRenderer().getCamera();
- final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
- // Make sure we are dragging over the arrow
- if (!_dragging) {
- findPick(oldMouse, camera);
- final Vector3 lastPick = getLastPick();
- if (lastPick == null) {
- return;
- } else {
- beginDrag(manager);
- }
- }
-
- // we've established that our mouse is being held down, and started over our arrow. So consume.
- inputConsumed.set(true);
-
- // check if we've moved at all
- if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
- return;
- }
-
- // act on drag
- final double scale = getNewScale(oldMouse, current, camera, manager);
-
- // Set new scale on spatial state
- manager.getSpatialState().getTransform().setScale(scale);
-
- // apply our filters, if any, now that we've made updates.
- applyFilters(manager);
- }
-
- protected double getNewScale(final Vector2 oldMouse, final MouseState current, final Camera camera,
- final InteractManager manager) {
- // calculate a plane running through the Arrow and facing the camera.
- _calcVec3A.set(_handle.getWorldTranslation());
- _calcVec3B.set(_calcVec3A).addLocal(camera.getLeft());
- _calcVec3C.set(_calcVec3A).addLocal(_arrowDirection);
- final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B, _calcVec3C);
-
- // find out where we were hitting the plane before
- getPickRay(oldMouse, camera);
- _calcRay.intersectsPlane(pickPlane, _calcVec3A);
- final double oldHeight = _calcVec3A.getY();
-
- // find out where we are hitting the plane now
- getPickRay(new Vector2(current.getX(), current.getY()), camera);
- _calcRay.intersectsPlane(pickPlane, _calcVec3A);
- final double newHeight = _calcVec3A.getY();
-
- // Use distance between points against arrow length to determine how big we need to grow our bounding radius
- final double delta = newHeight - oldHeight;
-
- final double oldRadius = manager.getSpatialTarget().getWorldBound().getRadius();
-
- return manager.getSpatialTarget().getScale().getY() * (1.0 + delta / oldRadius);
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.interact.widget;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ardor3d.extension.interact.InteractManager;
+import com.ardor3d.framework.Canvas;
+import com.ardor3d.input.ButtonState;
+import com.ardor3d.input.MouseState;
+import com.ardor3d.input.logical.TwoInputStates;
+import com.ardor3d.math.ColorRGBA;
+import com.ardor3d.math.Plane;
+import com.ardor3d.math.Quaternion;
+import com.ardor3d.math.Vector2;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyColorRGBA;
+import com.ardor3d.math.type.ReadOnlyVector3;
+import com.ardor3d.renderer.Camera;
+import com.ardor3d.renderer.Renderer;
+import com.ardor3d.renderer.queue.RenderBucketType;
+import com.ardor3d.renderer.state.BlendState;
+import com.ardor3d.renderer.state.ZBufferState;
+import com.ardor3d.renderer.state.ZBufferState.TestFunction;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.scenegraph.shape.Arrow;
+
+public class SimpleScaleWidget extends AbstractInteractWidget {
+ public static double MIN_SCALE = 0.000001;
+
+ protected ReadOnlyVector3 _arrowDirection;
+
+ public SimpleScaleWidget() {}
+
+ public SimpleScaleWidget withArrow(final ReadOnlyVector3 arrowDirection) {
+ return withArrow(arrowDirection, new ColorRGBA(1.0f, 0.0f, 0.0f, 0.4f), 0, 0);
+ }
+
+ public SimpleScaleWidget withArrow(final ReadOnlyVector3 arrowDirection, final ReadOnlyColorRGBA color) {
+ return withArrow(arrowDirection, color, 0, 0);
+ }
+
+ public SimpleScaleWidget withArrow(final ReadOnlyVector3 arrowDirection, final ReadOnlyColorRGBA color,
+ final double lengthGap, final double tipGap) {
+ _arrowDirection = new Vector3(arrowDirection);
+ _handle = new InteractArrow("scaleHandle", 1.0, 0.125, lengthGap, tipGap);
+ if (!_arrowDirection.equals(Vector3.UNIT_Z)) {
+ _handle.setRotation(new Quaternion().fromVectorToVector(Vector3.UNIT_Z, _arrowDirection));
+ }
+
+ final BlendState blend = new BlendState();
+ blend.setBlendEnabled(true);
+ _handle.setRenderState(blend);
+
+ ((Arrow) _handle).setDefaultColor(color);
+
+ final ZBufferState zstate = new ZBufferState();
+ zstate.setWritable(false);
+ zstate.setFunction(TestFunction.Always);
+ _handle.setRenderState(zstate);
+
+ _handle.getSceneHints().setRenderBucketType(RenderBucketType.PostBucket);
+ _handle.updateGeometricState(0);
+ return this;
+ }
+
+ @Override
+ public void targetChanged(final InteractManager manager) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ targetDataUpdated(manager);
+ }
+
+ @Override
+ public void targetDataUpdated(final InteractManager manager) {
+ final Spatial target = manager.getSpatialTarget();
+ if (target == null) {
+ _handle.setScale(1.0);
+ } else {
+ _handle.setScale(Math.max(SimpleScaleWidget.MIN_SCALE, target.getWorldBound().getRadius()));
+ }
+ }
+
+ @Override
+ public void render(final Renderer renderer, final InteractManager manager) {
+ final Spatial spat = manager.getSpatialTarget();
+ if (spat == null) {
+ return;
+ }
+
+ _handle.setTranslation(spat.getWorldTranslation());
+ _handle.updateGeometricState(0);
+
+ renderer.draw(_handle);
+ }
+
+ @Override
+ public void processInput(final Canvas source, final TwoInputStates inputStates, final AtomicBoolean inputConsumed,
+ final InteractManager manager) {
+ // Make sure we have something to modify
+ if (manager.getSpatialTarget() == null) {
+ return;
+ }
+
+ // Make sure we are dragging.
+ final MouseState current = inputStates.getCurrent().getMouseState();
+ final MouseState previous = inputStates.getPrevious().getMouseState();
+ if (current.getButtonState(_dragButton) != ButtonState.DOWN) {
+ if (_dragging) {
+ endDrag(manager);
+ }
+ return;
+ }
+ // if we're already dragging, make sure we only act on drags that started with a positive pick.
+ else if (!current.getButtonsPressedSince(previous).contains(_dragButton) && !_dragging) {
+ return;
+ }
+
+ final Camera camera = source.getCanvasRenderer().getCamera();
+ final Vector2 oldMouse = new Vector2(previous.getX(), previous.getY());
+ // Make sure we are dragging over the arrow
+ if (!_dragging) {
+ findPick(oldMouse, camera);
+ final Vector3 lastPick = getLastPick();
+ if (lastPick == null) {
+ return;
+ } else {
+ beginDrag(manager);
+ }
+ }
+
+ // we've established that our mouse is being held down, and started over our arrow. So consume.
+ inputConsumed.set(true);
+
+ // check if we've moved at all
+ if (current == previous || current.getDx() == 0 && current.getDy() == 0) {
+ return;
+ }
+
+ // act on drag
+ final double scale = getNewScale(oldMouse, current, camera, manager);
+
+ // Set new scale on spatial state
+ manager.getSpatialState().getTransform().setScale(scale);
+
+ // apply our filters, if any, now that we've made updates.
+ applyFilters(manager);
+ }
+
+ protected double getNewScale(final Vector2 oldMouse, final MouseState current, final Camera camera,
+ final InteractManager manager) {
+ // calculate a plane running through the Arrow and facing the camera.
+ _calcVec3A.set(_handle.getWorldTranslation());
+ _calcVec3B.set(_calcVec3A).addLocal(camera.getLeft());
+ _calcVec3C.set(_calcVec3A).addLocal(_arrowDirection);
+ final Plane pickPlane = new Plane().setPlanePoints(_calcVec3A, _calcVec3B, _calcVec3C);
+
+ // find out where we were hitting the plane before
+ getPickRay(oldMouse, camera);
+ _calcRay.intersectsPlane(pickPlane, _calcVec3A);
+ final double oldHeight = _calcVec3A.getY();
+
+ // find out where we are hitting the plane now
+ getPickRay(new Vector2(current.getX(), current.getY()), camera);
+ _calcRay.intersectsPlane(pickPlane, _calcVec3A);
+ final double newHeight = _calcVec3A.getY();
+
+ // Use distance between points against arrow length to determine how big we need to grow our bounding radius
+ final double delta = newHeight - oldHeight;
+
+ final double oldRadius = manager.getSpatialTarget().getWorldBound().getRadius();
+
+ return manager.getSpatialTarget().getScale().getY() * (1.0 + delta / oldRadius);
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java
index 52af183..a2c12b1 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2DataStore.java
@@ -1,52 +1,52 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.md2;
-
-import java.util.List;
-
-import com.ardor3d.extension.model.util.KeyframeController;
-import com.ardor3d.scenegraph.Mesh;
-import com.google.common.collect.Lists;
-
-public class Md2DataStore {
-
- private final Mesh _mainMesh;
- private final KeyframeController<Mesh> _controller;
-
- private final List<String> _frameNames = Lists.newArrayList();
-
- private final List<String> _skinNames = Lists.newArrayList();
-
- public Md2DataStore(final Mesh mainMesh, final KeyframeController<Mesh> controller) {
- _mainMesh = mainMesh;
- _controller = controller;
- }
-
- public Mesh getScene() {
- return _mainMesh;
- }
-
- public KeyframeController<Mesh> getController() {
- return _controller;
- }
-
- public List<String> getFrameNames() {
- return _frameNames;
- }
-
- public int getFrameIndex(final String frameName) {
- return _frameNames.indexOf(frameName);
- }
-
- public List<String> getSkinNames() {
- return _skinNames;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.md2;
+
+import java.util.List;
+
+import com.ardor3d.extension.model.util.KeyframeController;
+import com.ardor3d.scenegraph.Mesh;
+import com.google.common.collect.Lists;
+
+public class Md2DataStore {
+
+ private final Mesh _mainMesh;
+ private final KeyframeController<Mesh> _controller;
+
+ private final List<String> _frameNames = Lists.newArrayList();
+
+ private final List<String> _skinNames = Lists.newArrayList();
+
+ public Md2DataStore(final Mesh mainMesh, final KeyframeController<Mesh> controller) {
+ _mainMesh = mainMesh;
+ _controller = controller;
+ }
+
+ public Mesh getScene() {
+ return _mainMesh;
+ }
+
+ public KeyframeController<Mesh> getController() {
+ return _controller;
+ }
+
+ public List<String> getFrameNames() {
+ return _frameNames;
+ }
+
+ public int getFrameIndex(final String frameName) {
+ return _frameNames.indexOf(frameName);
+ }
+
+ public List<String> getSkinNames() {
+ return _skinNames;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java
index f6e75d2..8c6b8c1 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Frame.java
@@ -1,36 +1,36 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.md2;
-
-import com.ardor3d.math.Vector3;
-import com.ardor3d.math.type.ReadOnlyVector3;
-
-final class Md2Frame {
-
- /** frame name */
- String name; // char [16]
-
- /** scale factor */
- final Vector3 scale = new Vector3(1, 1, 1);
-
- /** translation vector */
- final Vector3 translate = new Vector3();
-
- /** vertex data */
- byte[] vertData;
-
- Md2Frame(final byte[] vertData, final String name, final ReadOnlyVector3 scale, final ReadOnlyVector3 translate) {
- this.vertData = vertData;
- this.scale.set(scale);
- this.translate.set(translate);
- this.name = name;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.md2;
+
+import com.ardor3d.math.Vector3;
+import com.ardor3d.math.type.ReadOnlyVector3;
+
+final class Md2Frame {
+
+ /** frame name */
+ String name; // char [16]
+
+ /** scale factor */
+ final Vector3 scale = new Vector3(1, 1, 1);
+
+ /** translation vector */
+ final Vector3 translate = new Vector3();
+
+ /** vertex data */
+ byte[] vertData;
+
+ Md2Frame(final byte[] vertData, final String name, final ReadOnlyVector3 scale, final ReadOnlyVector3 translate) {
+ this.vertData = vertData;
+ this.scale.set(scale);
+ this.translate.set(translate);
+ this.name = name;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java
index 992c018..2dc7bf7 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Header.java
@@ -1,78 +1,78 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.md2;
-
-/**
- * Header of MD2: see also http://tfc.duke.free.fr/coding/md2-specs-en.html
- */
-final class Md2Header {
- /** identifier of the file: magic number: "IDP2" */
- final int magic;
- /** version number of the file (must be 8) */
- final int version;
-
- /** texture width in pixels */
- final int skinWidth;
- /** texture height in pixels */
- final int skinHeight;
-
- /** size in bytes of a frame */
- final int frameSize;
-
- /** number of textures associated with the model */
- final int numSkins;
- /** number of vertices per frame */
- final int numVertices;
- /** number of texture coordinates */
- final int numTexCoords;
- /** number of triangles */
- final int numTriangles;
- /** number of gl commands */
- final int numGlCommands;
- /** number of animation frames */
- final int numFrames;
-
- /** offset in the file for the texture data */
- final int offsetSkins;
- /** offset in the file for the texture coords */
- final int offsetTexCoords;
- /** offset in the file for the face data */
- final int offsetTriangles;
- /** offset in the file for the frames data */
- final int offsetFrames;
- /** offset in the file for the gl commands data */
- final int offsetGlCommands;
- /** offset of EOF */
- final int offsetEnd;
-
- Md2Header(final int magic, final int version, final int skinWidth, final int skinHeight, final int frameSize,
- final int numSkins, final int numVertices, final int numTexCoords, final int numTriangles,
- final int numGlCommands, final int numFrames, final int offsetSkins, final int offsetTexCoords,
- final int offsetTriangles, final int offsetFrames, final int offsetGlCommands, final int offsetEnd) {
- this.magic = magic;
- this.version = version;
- this.skinWidth = skinWidth;
- this.skinHeight = skinHeight;
- this.frameSize = frameSize;
- this.numSkins = numSkins;
- this.numVertices = numVertices;
- this.numTexCoords = numTexCoords;
- this.numTriangles = numTriangles;
- this.numGlCommands = numGlCommands;
- this.numFrames = numFrames;
- this.offsetSkins = offsetSkins;
- this.offsetTexCoords = offsetTexCoords;
- this.offsetTriangles = offsetTriangles;
- this.offsetFrames = offsetFrames;
- this.offsetGlCommands = offsetGlCommands;
- this.offsetEnd = offsetEnd;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.md2;
+
+/**
+ * Header of MD2: see also http://tfc.duke.free.fr/coding/md2-specs-en.html
+ */
+final class Md2Header {
+ /** identifier of the file: magic number: "IDP2" */
+ final int magic;
+ /** version number of the file (must be 8) */
+ final int version;
+
+ /** texture width in pixels */
+ final int skinWidth;
+ /** texture height in pixels */
+ final int skinHeight;
+
+ /** size in bytes of a frame */
+ final int frameSize;
+
+ /** number of textures associated with the model */
+ final int numSkins;
+ /** number of vertices per frame */
+ final int numVertices;
+ /** number of texture coordinates */
+ final int numTexCoords;
+ /** number of triangles */
+ final int numTriangles;
+ /** number of gl commands */
+ final int numGlCommands;
+ /** number of animation frames */
+ final int numFrames;
+
+ /** offset in the file for the texture data */
+ final int offsetSkins;
+ /** offset in the file for the texture coords */
+ final int offsetTexCoords;
+ /** offset in the file for the face data */
+ final int offsetTriangles;
+ /** offset in the file for the frames data */
+ final int offsetFrames;
+ /** offset in the file for the gl commands data */
+ final int offsetGlCommands;
+ /** offset of EOF */
+ final int offsetEnd;
+
+ Md2Header(final int magic, final int version, final int skinWidth, final int skinHeight, final int frameSize,
+ final int numSkins, final int numVertices, final int numTexCoords, final int numTriangles,
+ final int numGlCommands, final int numFrames, final int offsetSkins, final int offsetTexCoords,
+ final int offsetTriangles, final int offsetFrames, final int offsetGlCommands, final int offsetEnd) {
+ this.magic = magic;
+ this.version = version;
+ this.skinWidth = skinWidth;
+ this.skinHeight = skinHeight;
+ this.frameSize = frameSize;
+ this.numSkins = numSkins;
+ this.numVertices = numVertices;
+ this.numTexCoords = numTexCoords;
+ this.numTriangles = numTriangles;
+ this.numGlCommands = numGlCommands;
+ this.numFrames = numFrames;
+ this.offsetSkins = offsetSkins;
+ this.offsetTexCoords = offsetTexCoords;
+ this.offsetTriangles = offsetTriangles;
+ this.offsetFrames = offsetFrames;
+ this.offsetGlCommands = offsetGlCommands;
+ this.offsetEnd = offsetEnd;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java
index 6fbc793..6270b66 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Importer.java
@@ -1,420 +1,420 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.md2;
-
-import java.io.InputStream;
-import java.util.List;
-
-import com.ardor3d.bounding.BoundingBox;
-import com.ardor3d.extension.model.util.KeyframeController;
-import com.ardor3d.image.Texture;
-import com.ardor3d.image.TextureStoreFormat;
-import com.ardor3d.image.Texture.MinificationFilter;
-import com.ardor3d.math.Vector3;
-import com.ardor3d.renderer.IndexMode;
-import com.ardor3d.renderer.state.TextureState;
-import com.ardor3d.scenegraph.FloatBufferData;
-import com.ardor3d.scenegraph.Mesh;
-import com.ardor3d.scenegraph.MeshData;
-import com.ardor3d.util.Ardor3dException;
-import com.ardor3d.util.LittleEndianRandomAccessDataInput;
-import com.ardor3d.util.TextureManager;
-import com.ardor3d.util.resource.ResourceLocator;
-import com.ardor3d.util.resource.ResourceLocatorTool;
-import com.ardor3d.util.resource.ResourceSource;
-import com.google.common.collect.Lists;
-
-public class Md2Importer {
-
- private final Vector3 calcVert = new Vector3();
-
- private boolean _loadTextures = true;
- private ResourceLocator _textureLocator;
- private ResourceLocator _modelLocator;
-
- // texture defaults
- private MinificationFilter _minificationFilter = MinificationFilter.Trilinear;
- private boolean _useCompression = true;
- private boolean _flipTextureVertically = false;
-
- public boolean isLoadTextures() {
- return _loadTextures;
- }
-
- public Md2Importer setLoadTextures(final boolean loadTextures) {
- _loadTextures = loadTextures;
- return this;
- }
-
- public Md2Importer setTextureLocator(final ResourceLocator locator) {
- _textureLocator = locator;
- return this;
- }
-
- public Md2Importer setModelLocator(final ResourceLocator locator) {
- _modelLocator = locator;
- return this;
- }
-
- public Md2Importer setFlipTextureVertically(final boolean flipTextureVertically) {
- _flipTextureVertically = flipTextureVertically;
- return this;
- }
-
- public boolean isFlipTextureVertically() {
- return _flipTextureVertically;
- }
-
- public Md2Importer setUseCompression(final boolean useCompression) {
- _useCompression = useCompression;
- return this;
- }
-
- public boolean isUseCompression() {
- return _useCompression;
- }
-
- public Md2Importer setMinificationFilter(final MinificationFilter minificationFilter) {
- _minificationFilter = minificationFilter;
- return this;
- }
-
- public MinificationFilter getMinificationFilter() {
- return _minificationFilter;
- }
-
- /**
- * Reads an MD2 file from the given resource
- *
- * @param resource
- * a resource pointing to the model we wish to load.
- * @return an Md2DataStore data object containing the scene and other useful elements.
- */
- public Md2DataStore load(final ResourceSource resource) {
- if (resource == null) {
- throw new NullPointerException("Unable to load null resource");
- }
-
- try {
- final InputStream md2Stream = resource.openStream();
- if (md2Stream == null) {
- throw new NullPointerException("Unable to load null streams");
- }
- // final Md2DataStore store = new Md2DataStore();
- final LittleEndianRandomAccessDataInput bis = new LittleEndianRandomAccessDataInput(md2Stream);
-
- // parse the header
- final Md2Header header = new Md2Header(bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis
- .readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis
- .readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis
- .readInt());
-
- // Check magic word and version
- if (header.magic != ('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') {
- throw new Ardor3dException("Not an MD2 file.");
- }
- if (header.version != 8) {
- throw new Ardor3dException("Invalid file version (Version not 8)!");
- }
-
- // Parse out texture names
- final String[] texNames = new String[header.numSkins];
- bis.seek(header.offsetSkins);
- for (int i = 0; i < header.numSkins; i++) {
- texNames[i] = bis.readString(64);
- }
-
- // Parse out tex coords
- final float[] texCoords = new float[2 * header.numTexCoords];
- bis.seek(header.offsetTexCoords);
- final float inverseWidth = 1f / header.skinWidth;
- final float inverseHeight = 1f / header.skinHeight;
- for (int i = 0; i < header.numTexCoords; i++) {
- texCoords[i * 2 + 0] = bis.readShort() * inverseWidth;
- texCoords[i * 2 + 1] = bis.readShort() * inverseHeight;
- }
-
- // Parse out triangles
- final short[] triangles = new short[header.numTriangles * 6];
- bis.seek(header.offsetTriangles);
- for (int i = 0; i < header.numTriangles; i++) {
- triangles[i * 6 + 0] = bis.readShort(); // vert index 0
- triangles[i * 6 + 1] = bis.readShort(); // vert index 1
- triangles[i * 6 + 2] = bis.readShort(); // vert index 2
- triangles[i * 6 + 3] = bis.readShort(); // texcoord index 0
- triangles[i * 6 + 4] = bis.readShort(); // texcoord index 1
- triangles[i * 6 + 5] = bis.readShort(); // texcoord index 2
- }
-
- // Parse out gl commands
- final Md2GlCommand[] commands = new Md2GlCommand[header.numGlCommands];
- bis.seek(header.offsetGlCommands);
- int length, absLength;
- Md2GlCommand cmd;
- final List<Integer> fanIndices = Lists.newArrayList();
- final List<Integer> stripIndices = Lists.newArrayList();
- for (int i = 0; i < header.numGlCommands; i++) {
- length = bis.readInt();
- if (length == 0) {
- break;
- }
- absLength = Math.abs(length);
- commands[i] = cmd = new Md2GlCommand(length >= 0 ? IndexMode.TriangleStrip : IndexMode.TriangleFan,
- absLength);
- if (cmd.mode == IndexMode.TriangleFan) {
- fanIndices.add(i);
- } else {
- stripIndices.add(i);
- }
- for (int j = 0; j < absLength; j++) {
- cmd.texCoords[j * 2 + 0] = bis.readFloat();
- cmd.texCoords[j * 2 + 1] = bis.readFloat();
- cmd.vertIndices[j] = bis.readInt();
- }
- }
-
- // Parse out frames
- final Md2Frame[] frames = new Md2Frame[header.numFrames];
- bis.seek(header.offsetFrames);
- final Vector3 scale = new Vector3();
- final Vector3 translate = new Vector3();
- for (int i = 0; i < header.numFrames; i++) {
- scale.set(bis.readFloat(), bis.readFloat(), bis.readFloat());
- translate.set(bis.readFloat(), bis.readFloat(), bis.readFloat());
- final String name = bis.readString(16);
- final byte[] vertData = new byte[header.numVertices * 4];
- bis.readFully(vertData);
- frames[i] = new Md2Frame(vertData, name, scale, translate);
- }
-
- // make index modes/counts to be used throughout meshes
- int vertexCount = 0;
- int fanIndex = stripIndices.size() != 0 ? 1 : 0;
- final IndexMode[] modes = new IndexMode[fanIndices.size() + fanIndex];
- final int[] counts = new int[modes.length];
- for (final Integer index : fanIndices) {
- counts[fanIndex] = commands[index].vertIndices.length;
- modes[fanIndex] = IndexMode.TriangleFan;
- vertexCount += counts[fanIndex];
- fanIndex++;
- }
- if (stripIndices.size() != 0) {
- int triCounts = 0;
- int vertCount;
- int extra = 0;
- for (final Integer index : stripIndices) {
- vertCount = commands[index].vertIndices.length;
- extra = vertCount % 2 == 1 ? 3 : 2;
- triCounts += vertCount + extra;
- }
- counts[0] = triCounts - extra + 1;
- modes[0] = IndexMode.TriangleStrip;
- vertexCount += counts[0];
- }
-
- vertexCount++;
-
- // Create each frame as a Mesh using glcommands if given
- final Mesh[] meshes = new Mesh[header.numFrames];
- MeshData mData;
- for (int i = 0; i < header.numFrames; i++) {
- final Md2Frame frame = frames[i];
-
- meshes[i] = new Mesh(frames[i].name);
- mData = meshes[i].getMeshData();
- mData.setIndexLengths(counts);
- mData.setIndexModes(modes);
-
- final FloatBufferData verts = new FloatBufferData(vertexCount * 3, 3);
- final FloatBufferData norms = new FloatBufferData(vertexCount * 3, 3);
- final FloatBufferData texs = new FloatBufferData(vertexCount * 3, 2);
- mData.setVertexCoords(verts);
- mData.setNormalCoords(norms);
- mData.setTextureCoords(texs, 0);
-
- // go through the triangle strips/fans and add them in
- // first the strips
- if (stripIndices.size() != 0) {
- for (int maxJ = stripIndices.size(), j = 0; j < maxJ; j++) {
- cmd = commands[stripIndices.get(j)];
- if (cmd.vertIndices.length < 3) {
- continue;
- }
-
- addVert(cmd, frame, 0, verts);
- norms.getBuffer().put(0).put(0).put(0);
- texs.getBuffer().put(0).put(0);
-
- // add strip verts / normals
- for (int k = 0; k < cmd.vertIndices.length; k++) {
- addVert(cmd, frame, k, verts);
- addNormal(cmd, frame, k, norms);
- }
-
- // add strip tex coords
- texs.getBuffer().put(cmd.texCoords);
-
- // if we're not the last strip, add a vert or two for degenerate triangle connector
- if (j != maxJ - 1) {
- addVert(cmd, frame, cmd.vertIndices.length - 1, verts);
- norms.getBuffer().put(0).put(0).put(0);
- texs.getBuffer().put(0).put(0);
- if (cmd.vertIndices.length % 2 == 1) {
- // extra vert to maintain wind order
- addVert(cmd, frame, cmd.vertIndices.length - 1, verts);
- norms.getBuffer().put(0).put(0).put(0);
- texs.getBuffer().put(0).put(0);
- }
- }
- }
- }
- // Now the fans
- // XXX: could add these to the strip instead
- for (final int j : fanIndices) {
- cmd = commands[j];
- texs.getBuffer().put(cmd.texCoords[0]).put(cmd.texCoords[1]);
- addNormal(cmd, frame, 0, norms);
- addVert(cmd, frame, 0, verts);
- for (int k = cmd.vertIndices.length; --k >= 1;) {
- texs.getBuffer().put(cmd.texCoords[k * 2]).put(cmd.texCoords[k * 2 + 1]);
- addNormal(cmd, frame, k, norms);
- addVert(cmd, frame, k, verts);
- }
- }
- }
-
- // Clone frame 0 as mesh for initial mesh
- final Mesh mesh = meshes[0].makeCopy(false);
- mesh.setModelBound(new BoundingBox());
-
- // Use resource name for mesh
- mesh.setName(resource.getName());
-
- // Add controller
- final KeyframeController<Mesh> controller = new KeyframeController<Mesh>();
- mesh.addController(controller);
- controller.setMorphingMesh(mesh);
- controller.setInterpTex(false);
- int i = 0;
- for (final Mesh meshX : meshes) {
- controller.setKeyframe(i, meshX);
- i++;
- }
-
- // Make a store object to return
- final Md2DataStore store = new Md2DataStore(mesh, controller);
-
- // store names
- for (final Md2Frame frame : frames) {
- store.getFrameNames().add(frame.name);
- }
-
- // store skin names
- for (final String name : texNames) {
- store.getSkinNames().add(name);
- }
-
- // Apply our texture
- if (isLoadTextures()) {
- Texture tex = null;
- for (final String name : texNames) {
- tex = loadTexture(name);
- if (tex != null) {
- break;
- }
- }
-
- // try using model name
- if (tex == null) {
- tex = loadTexture(resource.getName());
- }
-
- if (tex != null) {
- final TextureState ts = new TextureState();
- ts.setTexture(tex);
- mesh.setRenderState(ts);
- }
- }
-
- return store;
- } catch (final Exception e) {
- throw new Error("Unable to load md2 resource from URL: " + resource, e);
- }
- }
-
- private Texture loadTexture(final String name) {
- Texture tex = null;
- if (_textureLocator == null) {
- tex = TextureManager.load(name, getMinificationFilter(),
- isUseCompression() ? TextureStoreFormat.GuessCompressedFormat
- : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically());
- } else {
- final ResourceSource source = _textureLocator.locateResource(name);
- if (source != null) {
- tex = TextureManager.load(source, getMinificationFilter(),
- isUseCompression() ? TextureStoreFormat.GuessCompressedFormat
- : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically());
- }
- }
- return tex;
- }
-
- private void addNormal(final Md2GlCommand cmd, final Md2Frame frame, final int normalIndex,
- final FloatBufferData norms) {
- final int index = cmd.vertIndices[normalIndex];
- final byte[] vertData = frame.vertData;
- Md2Normals.getNormalVector(vertData[index * 4 + 3], calcVert);
- norms.getBuffer().put(calcVert.getXf()).put(calcVert.getYf()).put(calcVert.getZf());
- }
-
- private void addVert(final Md2GlCommand cmd, final Md2Frame frame, final int vertIndex, final FloatBufferData verts) {
- final int index = cmd.vertIndices[vertIndex];
- final byte[] vertData = frame.vertData;
- calcVert.set((vertData[index * 4 + 0] & 0xFF), (vertData[index * 4 + 1] & 0xFF),
- (vertData[index * 4 + 2] & 0xFF));
- calcVert.multiplyLocal(frame.scale).addLocal(frame.translate);
- verts.getBuffer().put(calcVert.getXf()).put(calcVert.getYf()).put(calcVert.getZf());
- }
-
- /**
- * Reads a MD2 file from the given resource
- *
- * @param resource
- * the name of the resource to find.
- * @return an ObjGeometryStore data object containing the scene and other useful elements.
- */
- public Md2DataStore load(final String resource) {
- final ResourceSource source;
- if (_modelLocator == null) {
- source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource);
- } else {
- source = _modelLocator.locateResource(resource);
- }
-
- if (source == null) {
- throw new Error("Unable to locate '" + resource + "'");
- }
-
- return load(source);
- }
-
- static class Md2GlCommand {
- IndexMode mode;
- float[] texCoords;
- int[] vertIndices;
-
- Md2GlCommand(final IndexMode indexMode, final int length) {
- mode = indexMode;
- texCoords = new float[length * 2];
- vertIndices = new int[length];
- }
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.md2;
+
+import java.io.InputStream;
+import java.util.List;
+
+import com.ardor3d.bounding.BoundingBox;
+import com.ardor3d.extension.model.util.KeyframeController;
+import com.ardor3d.image.Texture;
+import com.ardor3d.image.TextureStoreFormat;
+import com.ardor3d.image.Texture.MinificationFilter;
+import com.ardor3d.math.Vector3;
+import com.ardor3d.renderer.IndexMode;
+import com.ardor3d.renderer.state.TextureState;
+import com.ardor3d.scenegraph.FloatBufferData;
+import com.ardor3d.scenegraph.Mesh;
+import com.ardor3d.scenegraph.MeshData;
+import com.ardor3d.util.Ardor3dException;
+import com.ardor3d.util.LittleEndianRandomAccessDataInput;
+import com.ardor3d.util.TextureManager;
+import com.ardor3d.util.resource.ResourceLocator;
+import com.ardor3d.util.resource.ResourceLocatorTool;
+import com.ardor3d.util.resource.ResourceSource;
+import com.google.common.collect.Lists;
+
+public class Md2Importer {
+
+ private final Vector3 calcVert = new Vector3();
+
+ private boolean _loadTextures = true;
+ private ResourceLocator _textureLocator;
+ private ResourceLocator _modelLocator;
+
+ // texture defaults
+ private MinificationFilter _minificationFilter = MinificationFilter.Trilinear;
+ private boolean _useCompression = true;
+ private boolean _flipTextureVertically = false;
+
+ public boolean isLoadTextures() {
+ return _loadTextures;
+ }
+
+ public Md2Importer setLoadTextures(final boolean loadTextures) {
+ _loadTextures = loadTextures;
+ return this;
+ }
+
+ public Md2Importer setTextureLocator(final ResourceLocator locator) {
+ _textureLocator = locator;
+ return this;
+ }
+
+ public Md2Importer setModelLocator(final ResourceLocator locator) {
+ _modelLocator = locator;
+ return this;
+ }
+
+ public Md2Importer setFlipTextureVertically(final boolean flipTextureVertically) {
+ _flipTextureVertically = flipTextureVertically;
+ return this;
+ }
+
+ public boolean isFlipTextureVertically() {
+ return _flipTextureVertically;
+ }
+
+ public Md2Importer setUseCompression(final boolean useCompression) {
+ _useCompression = useCompression;
+ return this;
+ }
+
+ public boolean isUseCompression() {
+ return _useCompression;
+ }
+
+ public Md2Importer setMinificationFilter(final MinificationFilter minificationFilter) {
+ _minificationFilter = minificationFilter;
+ return this;
+ }
+
+ public MinificationFilter getMinificationFilter() {
+ return _minificationFilter;
+ }
+
+ /**
+ * Reads an MD2 file from the given resource
+ *
+ * @param resource
+ * a resource pointing to the model we wish to load.
+ * @return an Md2DataStore data object containing the scene and other useful elements.
+ */
+ public Md2DataStore load(final ResourceSource resource) {
+ if (resource == null) {
+ throw new NullPointerException("Unable to load null resource");
+ }
+
+ try {
+ final InputStream md2Stream = resource.openStream();
+ if (md2Stream == null) {
+ throw new NullPointerException("Unable to load null streams");
+ }
+ // final Md2DataStore store = new Md2DataStore();
+ final LittleEndianRandomAccessDataInput bis = new LittleEndianRandomAccessDataInput(md2Stream);
+
+ // parse the header
+ final Md2Header header = new Md2Header(bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis
+ .readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis
+ .readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis.readInt(), bis
+ .readInt());
+
+ // Check magic word and version
+ if (header.magic != ('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') {
+ throw new Ardor3dException("Not an MD2 file.");
+ }
+ if (header.version != 8) {
+ throw new Ardor3dException("Invalid file version (Version not 8)!");
+ }
+
+ // Parse out texture names
+ final String[] texNames = new String[header.numSkins];
+ bis.seek(header.offsetSkins);
+ for (int i = 0; i < header.numSkins; i++) {
+ texNames[i] = bis.readString(64);
+ }
+
+ // Parse out tex coords
+ final float[] texCoords = new float[2 * header.numTexCoords];
+ bis.seek(header.offsetTexCoords);
+ final float inverseWidth = 1f / header.skinWidth;
+ final float inverseHeight = 1f / header.skinHeight;
+ for (int i = 0; i < header.numTexCoords; i++) {
+ texCoords[i * 2 + 0] = bis.readShort() * inverseWidth;
+ texCoords[i * 2 + 1] = bis.readShort() * inverseHeight;
+ }
+
+ // Parse out triangles
+ final short[] triangles = new short[header.numTriangles * 6];
+ bis.seek(header.offsetTriangles);
+ for (int i = 0; i < header.numTriangles; i++) {
+ triangles[i * 6 + 0] = bis.readShort(); // vert index 0
+ triangles[i * 6 + 1] = bis.readShort(); // vert index 1
+ triangles[i * 6 + 2] = bis.readShort(); // vert index 2
+ triangles[i * 6 + 3] = bis.readShort(); // texcoord index 0
+ triangles[i * 6 + 4] = bis.readShort(); // texcoord index 1
+ triangles[i * 6 + 5] = bis.readShort(); // texcoord index 2
+ }
+
+ // Parse out gl commands
+ final Md2GlCommand[] commands = new Md2GlCommand[header.numGlCommands];
+ bis.seek(header.offsetGlCommands);
+ int length, absLength;
+ Md2GlCommand cmd;
+ final List<Integer> fanIndices = Lists.newArrayList();
+ final List<Integer> stripIndices = Lists.newArrayList();
+ for (int i = 0; i < header.numGlCommands; i++) {
+ length = bis.readInt();
+ if (length == 0) {
+ break;
+ }
+ absLength = Math.abs(length);
+ commands[i] = cmd = new Md2GlCommand(length >= 0 ? IndexMode.TriangleStrip : IndexMode.TriangleFan,
+ absLength);
+ if (cmd.mode == IndexMode.TriangleFan) {
+ fanIndices.add(i);
+ } else {
+ stripIndices.add(i);
+ }
+ for (int j = 0; j < absLength; j++) {
+ cmd.texCoords[j * 2 + 0] = bis.readFloat();
+ cmd.texCoords[j * 2 + 1] = bis.readFloat();
+ cmd.vertIndices[j] = bis.readInt();
+ }
+ }
+
+ // Parse out frames
+ final Md2Frame[] frames = new Md2Frame[header.numFrames];
+ bis.seek(header.offsetFrames);
+ final Vector3 scale = new Vector3();
+ final Vector3 translate = new Vector3();
+ for (int i = 0; i < header.numFrames; i++) {
+ scale.set(bis.readFloat(), bis.readFloat(), bis.readFloat());
+ translate.set(bis.readFloat(), bis.readFloat(), bis.readFloat());
+ final String name = bis.readString(16);
+ final byte[] vertData = new byte[header.numVertices * 4];
+ bis.readFully(vertData);
+ frames[i] = new Md2Frame(vertData, name, scale, translate);
+ }
+
+ // make index modes/counts to be used throughout meshes
+ int vertexCount = 0;
+ int fanIndex = stripIndices.size() != 0 ? 1 : 0;
+ final IndexMode[] modes = new IndexMode[fanIndices.size() + fanIndex];
+ final int[] counts = new int[modes.length];
+ for (final Integer index : fanIndices) {
+ counts[fanIndex] = commands[index].vertIndices.length;
+ modes[fanIndex] = IndexMode.TriangleFan;
+ vertexCount += counts[fanIndex];
+ fanIndex++;
+ }
+ if (stripIndices.size() != 0) {
+ int triCounts = 0;
+ int vertCount;
+ int extra = 0;
+ for (final Integer index : stripIndices) {
+ vertCount = commands[index].vertIndices.length;
+ extra = vertCount % 2 == 1 ? 3 : 2;
+ triCounts += vertCount + extra;
+ }
+ counts[0] = triCounts - extra + 1;
+ modes[0] = IndexMode.TriangleStrip;
+ vertexCount += counts[0];
+ }
+
+ vertexCount++;
+
+ // Create each frame as a Mesh using glcommands if given
+ final Mesh[] meshes = new Mesh[header.numFrames];
+ MeshData mData;
+ for (int i = 0; i < header.numFrames; i++) {
+ final Md2Frame frame = frames[i];
+
+ meshes[i] = new Mesh(frames[i].name);
+ mData = meshes[i].getMeshData();
+ mData.setIndexLengths(counts);
+ mData.setIndexModes(modes);
+
+ final FloatBufferData verts = new FloatBufferData(vertexCount * 3, 3);
+ final FloatBufferData norms = new FloatBufferData(vertexCount * 3, 3);
+ final FloatBufferData texs = new FloatBufferData(vertexCount * 3, 2);
+ mData.setVertexCoords(verts);
+ mData.setNormalCoords(norms);
+ mData.setTextureCoords(texs, 0);
+
+ // go through the triangle strips/fans and add them in
+ // first the strips
+ if (stripIndices.size() != 0) {
+ for (int maxJ = stripIndices.size(), j = 0; j < maxJ; j++) {
+ cmd = commands[stripIndices.get(j)];
+ if (cmd.vertIndices.length < 3) {
+ continue;
+ }
+
+ addVert(cmd, frame, 0, verts);
+ norms.getBuffer().put(0).put(0).put(0);
+ texs.getBuffer().put(0).put(0);
+
+ // add strip verts / normals
+ for (int k = 0; k < cmd.vertIndices.length; k++) {
+ addVert(cmd, frame, k, verts);
+ addNormal(cmd, frame, k, norms);
+ }
+
+ // add strip tex coords
+ texs.getBuffer().put(cmd.texCoords);
+
+ // if we're not the last strip, add a vert or two for degenerate triangle connector
+ if (j != maxJ - 1) {
+ addVert(cmd, frame, cmd.vertIndices.length - 1, verts);
+ norms.getBuffer().put(0).put(0).put(0);
+ texs.getBuffer().put(0).put(0);
+ if (cmd.vertIndices.length % 2 == 1) {
+ // extra vert to maintain wind order
+ addVert(cmd, frame, cmd.vertIndices.length - 1, verts);
+ norms.getBuffer().put(0).put(0).put(0);
+ texs.getBuffer().put(0).put(0);
+ }
+ }
+ }
+ }
+ // Now the fans
+ // XXX: could add these to the strip instead
+ for (final int j : fanIndices) {
+ cmd = commands[j];
+ texs.getBuffer().put(cmd.texCoords[0]).put(cmd.texCoords[1]);
+ addNormal(cmd, frame, 0, norms);
+ addVert(cmd, frame, 0, verts);
+ for (int k = cmd.vertIndices.length; --k >= 1;) {
+ texs.getBuffer().put(cmd.texCoords[k * 2]).put(cmd.texCoords[k * 2 + 1]);
+ addNormal(cmd, frame, k, norms);
+ addVert(cmd, frame, k, verts);
+ }
+ }
+ }
+
+ // Clone frame 0 as mesh for initial mesh
+ final Mesh mesh = meshes[0].makeCopy(false);
+ mesh.setModelBound(new BoundingBox());
+
+ // Use resource name for mesh
+ mesh.setName(resource.getName());
+
+ // Add controller
+ final KeyframeController<Mesh> controller = new KeyframeController<Mesh>();
+ mesh.addController(controller);
+ controller.setMorphingMesh(mesh);
+ controller.setInterpTex(false);
+ int i = 0;
+ for (final Mesh meshX : meshes) {
+ controller.setKeyframe(i, meshX);
+ i++;
+ }
+
+ // Make a store object to return
+ final Md2DataStore store = new Md2DataStore(mesh, controller);
+
+ // store names
+ for (final Md2Frame frame : frames) {
+ store.getFrameNames().add(frame.name);
+ }
+
+ // store skin names
+ for (final String name : texNames) {
+ store.getSkinNames().add(name);
+ }
+
+ // Apply our texture
+ if (isLoadTextures()) {
+ Texture tex = null;
+ for (final String name : texNames) {
+ tex = loadTexture(name);
+ if (tex != null) {
+ break;
+ }
+ }
+
+ // try using model name
+ if (tex == null) {
+ tex = loadTexture(resource.getName());
+ }
+
+ if (tex != null) {
+ final TextureState ts = new TextureState();
+ ts.setTexture(tex);
+ mesh.setRenderState(ts);
+ }
+ }
+
+ return store;
+ } catch (final Exception e) {
+ throw new Error("Unable to load md2 resource from URL: " + resource, e);
+ }
+ }
+
+ private Texture loadTexture(final String name) {
+ Texture tex = null;
+ if (_textureLocator == null) {
+ tex = TextureManager.load(name, getMinificationFilter(),
+ isUseCompression() ? TextureStoreFormat.GuessCompressedFormat
+ : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically());
+ } else {
+ final ResourceSource source = _textureLocator.locateResource(name);
+ if (source != null) {
+ tex = TextureManager.load(source, getMinificationFilter(),
+ isUseCompression() ? TextureStoreFormat.GuessCompressedFormat
+ : TextureStoreFormat.GuessNoCompressedFormat, isFlipTextureVertically());
+ }
+ }
+ return tex;
+ }
+
+ private void addNormal(final Md2GlCommand cmd, final Md2Frame frame, final int normalIndex,
+ final FloatBufferData norms) {
+ final int index = cmd.vertIndices[normalIndex];
+ final byte[] vertData = frame.vertData;
+ Md2Normals.getNormalVector(vertData[index * 4 + 3], calcVert);
+ norms.getBuffer().put(calcVert.getXf()).put(calcVert.getYf()).put(calcVert.getZf());
+ }
+
+ private void addVert(final Md2GlCommand cmd, final Md2Frame frame, final int vertIndex, final FloatBufferData verts) {
+ final int index = cmd.vertIndices[vertIndex];
+ final byte[] vertData = frame.vertData;
+ calcVert.set((vertData[index * 4 + 0] & 0xFF), (vertData[index * 4 + 1] & 0xFF),
+ (vertData[index * 4 + 2] & 0xFF));
+ calcVert.multiplyLocal(frame.scale).addLocal(frame.translate);
+ verts.getBuffer().put(calcVert.getXf()).put(calcVert.getYf()).put(calcVert.getZf());
+ }
+
+ /**
+ * Reads a MD2 file from the given resource
+ *
+ * @param resource
+ * the name of the resource to find.
+ * @return an ObjGeometryStore data object containing the scene and other useful elements.
+ */
+ public Md2DataStore load(final String resource) {
+ final ResourceSource source;
+ if (_modelLocator == null) {
+ source = ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_MODEL, resource);
+ } else {
+ source = _modelLocator.locateResource(resource);
+ }
+
+ if (source == null) {
+ throw new Error("Unable to locate '" + resource + "'");
+ }
+
+ return load(source);
+ }
+
+ static class Md2GlCommand {
+ IndexMode mode;
+ float[] texCoords;
+ int[] vertIndices;
+
+ Md2GlCommand(final IndexMode indexMode, final int length) {
+ mode = indexMode;
+ texCoords = new float[length * 2];
+ vertIndices = new int[length];
+ }
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java
index 20afdc2..fcb0f86 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/md2/Md2Normals.java
@@ -1,190 +1,190 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.md2;
-
-import com.ardor3d.math.Vector3;
-
-final class Md2Normals {
- /** Pre-generated md2 normals. See also http://tfc.duke.free.fr/coding/src/anorms.h */
- final static float[] NORMALS = { //
- -0.525731f, 0.000000f, 0.850651f, //
- -0.442863f, 0.238856f, 0.864188f, //
- -0.295242f, 0.000000f, 0.955423f, //
- -0.309017f, 0.500000f, 0.809017f, //
- -0.162460f, 0.262866f, 0.951056f, //
- 0.000000f, 0.000000f, 1.000000f, //
- 0.000000f, 0.850651f, 0.525731f, //
- -0.147621f, 0.716567f, 0.681718f, //
- 0.147621f, 0.716567f, 0.681718f, //
- 0.000000f, 0.525731f, 0.850651f, //
- 0.309017f, 0.500000f, 0.809017f, //
- 0.525731f, 0.000000f, 0.850651f, //
- 0.295242f, 0.000000f, 0.955423f, //
- 0.442863f, 0.238856f, 0.864188f, //
- 0.162460f, 0.262866f, 0.951056f, //
- -0.681718f, 0.147621f, 0.716567f, //
- -0.809017f, 0.309017f, 0.500000f, //
- -0.587785f, 0.425325f, 0.688191f, //
- -0.850651f, 0.525731f, 0.000000f, //
- -0.864188f, 0.442863f, 0.238856f, //
- -0.716567f, 0.681718f, 0.147621f, //
- -0.688191f, 0.587785f, 0.425325f, //
- -0.500000f, 0.809017f, 0.309017f, //
- -0.238856f, 0.864188f, 0.442863f, //
- -0.425325f, 0.688191f, 0.587785f, //
- -0.716567f, 0.681718f, -0.147621f, //
- -0.500000f, 0.809017f, -0.309017f, //
- -0.525731f, 0.850651f, 0.000000f, //
- 0.000000f, 0.850651f, -0.525731f, //
- -0.238856f, 0.864188f, -0.442863f, //
- 0.000000f, 0.955423f, -0.295242f, //
- -0.262866f, 0.951056f, -0.162460f, //
- 0.000000f, 1.000000f, 0.000000f, //
- 0.000000f, 0.955423f, 0.295242f, //
- -0.262866f, 0.951056f, 0.162460f, //
- 0.238856f, 0.864188f, 0.442863f, //
- 0.262866f, 0.951056f, 0.162460f, //
- 0.500000f, 0.809017f, 0.309017f, //
- 0.238856f, 0.864188f, -0.442863f, //
- 0.262866f, 0.951056f, -0.162460f, //
- 0.500000f, 0.809017f, -0.309017f, //
- 0.850651f, 0.525731f, 0.000000f, //
- 0.716567f, 0.681718f, 0.147621f, //
- 0.716567f, 0.681718f, -0.147621f, //
- 0.525731f, 0.850651f, 0.000000f, //
- 0.425325f, 0.688191f, 0.587785f, //
- 0.864188f, 0.442863f, 0.238856f, //
- 0.688191f, 0.587785f, 0.425325f, //
- 0.809017f, 0.309017f, 0.500000f, //
- 0.681718f, 0.147621f, 0.716567f, //
- 0.587785f, 0.425325f, 0.688191f, //
- 0.955423f, 0.295242f, 0.000000f, //
- 1.000000f, 0.000000f, 0.000000f, //
- 0.951056f, 0.162460f, 0.262866f, //
- 0.850651f, -0.525731f, 0.000000f, //
- 0.955423f, -0.295242f, 0.000000f, //
- 0.864188f, -0.442863f, 0.238856f, //
- 0.951056f, -0.162460f, 0.262866f, //
- 0.809017f, -0.309017f, 0.500000f, //
- 0.681718f, -0.147621f, 0.716567f, //
- 0.850651f, 0.000000f, 0.525731f, //
- 0.864188f, 0.442863f, -0.238856f, //
- 0.809017f, 0.309017f, -0.500000f, //
- 0.951056f, 0.162460f, -0.262866f, //
- 0.525731f, 0.000000f, -0.850651f, //
- 0.681718f, 0.147621f, -0.716567f, //
- 0.681718f, -0.147621f, -0.716567f, //
- 0.850651f, 0.000000f, -0.525731f, //
- 0.809017f, -0.309017f, -0.500000f, //
- 0.864188f, -0.442863f, -0.238856f, //
- 0.951056f, -0.162460f, -0.262866f, //
- 0.147621f, 0.716567f, -0.681718f, //
- 0.309017f, 0.500000f, -0.809017f, //
- 0.425325f, 0.688191f, -0.587785f, //
- 0.442863f, 0.238856f, -0.864188f, //
- 0.587785f, 0.425325f, -0.688191f, //
- 0.688191f, 0.587785f, -0.425325f, //
- -0.147621f, 0.716567f, -0.681718f, //
- -0.309017f, 0.500000f, -0.809017f, //
- 0.000000f, 0.525731f, -0.850651f, //
- -0.525731f, 0.000000f, -0.850651f, //
- -0.442863f, 0.238856f, -0.864188f, //
- -0.295242f, 0.000000f, -0.955423f, //
- -0.162460f, 0.262866f, -0.951056f, //
- 0.000000f, 0.000000f, -1.000000f, //
- 0.295242f, 0.000000f, -0.955423f, //
- 0.162460f, 0.262866f, -0.951056f, //
- -0.442863f, -0.238856f, -0.864188f, //
- -0.309017f, -0.500000f, -0.809017f, //
- -0.162460f, -0.262866f, -0.951056f, //
- 0.000000f, -0.850651f, -0.525731f, //
- -0.147621f, -0.716567f, -0.681718f, //
- 0.147621f, -0.716567f, -0.681718f, //
- 0.000000f, -0.525731f, -0.850651f, //
- 0.309017f, -0.500000f, -0.809017f, //
- 0.442863f, -0.238856f, -0.864188f, //
- 0.162460f, -0.262866f, -0.951056f, //
- 0.238856f, -0.864188f, -0.442863f, //
- 0.500000f, -0.809017f, -0.309017f, //
- 0.425325f, -0.688191f, -0.587785f, //
- 0.716567f, -0.681718f, -0.147621f, //
- 0.688191f, -0.587785f, -0.425325f, //
- 0.587785f, -0.425325f, -0.688191f, //
- 0.000000f, -0.955423f, -0.295242f, //
- 0.000000f, -1.000000f, 0.000000f, //
- 0.262866f, -0.951056f, -0.162460f, //
- 0.000000f, -0.850651f, 0.525731f, //
- 0.000000f, -0.955423f, 0.295242f, //
- 0.238856f, -0.864188f, 0.442863f, //
- 0.262866f, -0.951056f, 0.162460f, //
- 0.500000f, -0.809017f, 0.309017f, //
- 0.716567f, -0.681718f, 0.147621f, //
- 0.525731f, -0.850651f, 0.000000f, //
- -0.238856f, -0.864188f, -0.442863f, //
- -0.500000f, -0.809017f, -0.309017f, //
- -0.262866f, -0.951056f, -0.162460f, //
- -0.850651f, -0.525731f, 0.000000f, //
- -0.716567f, -0.681718f, -0.147621f, //
- -0.716567f, -0.681718f, 0.147621f, //
- -0.525731f, -0.850651f, 0.000000f, //
- -0.500000f, -0.809017f, 0.309017f, //
- -0.238856f, -0.864188f, 0.442863f, //
- -0.262866f, -0.951056f, 0.162460f, //
- -0.864188f, -0.442863f, 0.238856f, //
- -0.809017f, -0.309017f, 0.500000f, //
- -0.688191f, -0.587785f, 0.425325f, //
- -0.681718f, -0.147621f, 0.716567f, //
- -0.442863f, -0.238856f, 0.864188f, //
- -0.587785f, -0.425325f, 0.688191f, //
- -0.309017f, -0.500000f, 0.809017f, //
- -0.147621f, -0.716567f, 0.681718f, //
- -0.425325f, -0.688191f, 0.587785f, //
- -0.162460f, -0.262866f, 0.951056f, //
- 0.442863f, -0.238856f, 0.864188f, //
- 0.162460f, -0.262866f, 0.951056f, //
- 0.309017f, -0.500000f, 0.809017f, //
- 0.147621f, -0.716567f, 0.681718f, //
- 0.000000f, -0.525731f, 0.850651f, //
- 0.425325f, -0.688191f, 0.587785f, //
- 0.587785f, -0.425325f, 0.688191f, //
- 0.688191f, -0.587785f, 0.425325f, //
- -0.955423f, 0.295242f, 0.000000f, //
- -0.951056f, 0.162460f, 0.262866f, //
- -1.000000f, 0.000000f, 0.000000f, //
- -0.850651f, 0.000000f, 0.525731f, //
- -0.955423f, -0.295242f, 0.000000f, //
- -0.951056f, -0.162460f, 0.262866f, //
- -0.864188f, 0.442863f, -0.238856f, //
- -0.951056f, 0.162460f, -0.262866f, //
- -0.809017f, 0.309017f, -0.500000f, //
- -0.864188f, -0.442863f, -0.238856f, //
- -0.951056f, -0.162460f, -0.262866f, //
- -0.809017f, -0.309017f, -0.500000f, //
- -0.681718f, 0.147621f, -0.716567f, //
- -0.681718f, -0.147621f, -0.716567f, //
- -0.850651f, 0.000000f, -0.525731f, //
- -0.688191f, 0.587785f, -0.425325f, //
- -0.587785f, 0.425325f, -0.688191f, //
- -0.425325f, 0.688191f, -0.587785f, //
- -0.425325f, -0.688191f, -0.587785f, //
- -0.587785f, -0.425325f, -0.688191f, //
- -0.688191f, -0.587785f, -0.425325f //
- };
-
- public static void getNormalVector(final byte i, final Vector3 store) {
- final int index = 3 * (i & 0xff);
- if (index < 0 || index > Md2Normals.NORMALS.length) {
- store.set(0, 1, 0);
- } else {
- store.set(Md2Normals.NORMALS[index + 0], Md2Normals.NORMALS[index + 1], Md2Normals.NORMALS[index + 2]);
- }
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.md2;
+
+import com.ardor3d.math.Vector3;
+
+final class Md2Normals {
+ /** Pre-generated md2 normals. See also http://tfc.duke.free.fr/coding/src/anorms.h */
+ final static float[] NORMALS = { //
+ -0.525731f, 0.000000f, 0.850651f, //
+ -0.442863f, 0.238856f, 0.864188f, //
+ -0.295242f, 0.000000f, 0.955423f, //
+ -0.309017f, 0.500000f, 0.809017f, //
+ -0.162460f, 0.262866f, 0.951056f, //
+ 0.000000f, 0.000000f, 1.000000f, //
+ 0.000000f, 0.850651f, 0.525731f, //
+ -0.147621f, 0.716567f, 0.681718f, //
+ 0.147621f, 0.716567f, 0.681718f, //
+ 0.000000f, 0.525731f, 0.850651f, //
+ 0.309017f, 0.500000f, 0.809017f, //
+ 0.525731f, 0.000000f, 0.850651f, //
+ 0.295242f, 0.000000f, 0.955423f, //
+ 0.442863f, 0.238856f, 0.864188f, //
+ 0.162460f, 0.262866f, 0.951056f, //
+ -0.681718f, 0.147621f, 0.716567f, //
+ -0.809017f, 0.309017f, 0.500000f, //
+ -0.587785f, 0.425325f, 0.688191f, //
+ -0.850651f, 0.525731f, 0.000000f, //
+ -0.864188f, 0.442863f, 0.238856f, //
+ -0.716567f, 0.681718f, 0.147621f, //
+ -0.688191f, 0.587785f, 0.425325f, //
+ -0.500000f, 0.809017f, 0.309017f, //
+ -0.238856f, 0.864188f, 0.442863f, //
+ -0.425325f, 0.688191f, 0.587785f, //
+ -0.716567f, 0.681718f, -0.147621f, //
+ -0.500000f, 0.809017f, -0.309017f, //
+ -0.525731f, 0.850651f, 0.000000f, //
+ 0.000000f, 0.850651f, -0.525731f, //
+ -0.238856f, 0.864188f, -0.442863f, //
+ 0.000000f, 0.955423f, -0.295242f, //
+ -0.262866f, 0.951056f, -0.162460f, //
+ 0.000000f, 1.000000f, 0.000000f, //
+ 0.000000f, 0.955423f, 0.295242f, //
+ -0.262866f, 0.951056f, 0.162460f, //
+ 0.238856f, 0.864188f, 0.442863f, //
+ 0.262866f, 0.951056f, 0.162460f, //
+ 0.500000f, 0.809017f, 0.309017f, //
+ 0.238856f, 0.864188f, -0.442863f, //
+ 0.262866f, 0.951056f, -0.162460f, //
+ 0.500000f, 0.809017f, -0.309017f, //
+ 0.850651f, 0.525731f, 0.000000f, //
+ 0.716567f, 0.681718f, 0.147621f, //
+ 0.716567f, 0.681718f, -0.147621f, //
+ 0.525731f, 0.850651f, 0.000000f, //
+ 0.425325f, 0.688191f, 0.587785f, //
+ 0.864188f, 0.442863f, 0.238856f, //
+ 0.688191f, 0.587785f, 0.425325f, //
+ 0.809017f, 0.309017f, 0.500000f, //
+ 0.681718f, 0.147621f, 0.716567f, //
+ 0.587785f, 0.425325f, 0.688191f, //
+ 0.955423f, 0.295242f, 0.000000f, //
+ 1.000000f, 0.000000f, 0.000000f, //
+ 0.951056f, 0.162460f, 0.262866f, //
+ 0.850651f, -0.525731f, 0.000000f, //
+ 0.955423f, -0.295242f, 0.000000f, //
+ 0.864188f, -0.442863f, 0.238856f, //
+ 0.951056f, -0.162460f, 0.262866f, //
+ 0.809017f, -0.309017f, 0.500000f, //
+ 0.681718f, -0.147621f, 0.716567f, //
+ 0.850651f, 0.000000f, 0.525731f, //
+ 0.864188f, 0.442863f, -0.238856f, //
+ 0.809017f, 0.309017f, -0.500000f, //
+ 0.951056f, 0.162460f, -0.262866f, //
+ 0.525731f, 0.000000f, -0.850651f, //
+ 0.681718f, 0.147621f, -0.716567f, //
+ 0.681718f, -0.147621f, -0.716567f, //
+ 0.850651f, 0.000000f, -0.525731f, //
+ 0.809017f, -0.309017f, -0.500000f, //
+ 0.864188f, -0.442863f, -0.238856f, //
+ 0.951056f, -0.162460f, -0.262866f, //
+ 0.147621f, 0.716567f, -0.681718f, //
+ 0.309017f, 0.500000f, -0.809017f, //
+ 0.425325f, 0.688191f, -0.587785f, //
+ 0.442863f, 0.238856f, -0.864188f, //
+ 0.587785f, 0.425325f, -0.688191f, //
+ 0.688191f, 0.587785f, -0.425325f, //
+ -0.147621f, 0.716567f, -0.681718f, //
+ -0.309017f, 0.500000f, -0.809017f, //
+ 0.000000f, 0.525731f, -0.850651f, //
+ -0.525731f, 0.000000f, -0.850651f, //
+ -0.442863f, 0.238856f, -0.864188f, //
+ -0.295242f, 0.000000f, -0.955423f, //
+ -0.162460f, 0.262866f, -0.951056f, //
+ 0.000000f, 0.000000f, -1.000000f, //
+ 0.295242f, 0.000000f, -0.955423f, //
+ 0.162460f, 0.262866f, -0.951056f, //
+ -0.442863f, -0.238856f, -0.864188f, //
+ -0.309017f, -0.500000f, -0.809017f, //
+ -0.162460f, -0.262866f, -0.951056f, //
+ 0.000000f, -0.850651f, -0.525731f, //
+ -0.147621f, -0.716567f, -0.681718f, //
+ 0.147621f, -0.716567f, -0.681718f, //
+ 0.000000f, -0.525731f, -0.850651f, //
+ 0.309017f, -0.500000f, -0.809017f, //
+ 0.442863f, -0.238856f, -0.864188f, //
+ 0.162460f, -0.262866f, -0.951056f, //
+ 0.238856f, -0.864188f, -0.442863f, //
+ 0.500000f, -0.809017f, -0.309017f, //
+ 0.425325f, -0.688191f, -0.587785f, //
+ 0.716567f, -0.681718f, -0.147621f, //
+ 0.688191f, -0.587785f, -0.425325f, //
+ 0.587785f, -0.425325f, -0.688191f, //
+ 0.000000f, -0.955423f, -0.295242f, //
+ 0.000000f, -1.000000f, 0.000000f, //
+ 0.262866f, -0.951056f, -0.162460f, //
+ 0.000000f, -0.850651f, 0.525731f, //
+ 0.000000f, -0.955423f, 0.295242f, //
+ 0.238856f, -0.864188f, 0.442863f, //
+ 0.262866f, -0.951056f, 0.162460f, //
+ 0.500000f, -0.809017f, 0.309017f, //
+ 0.716567f, -0.681718f, 0.147621f, //
+ 0.525731f, -0.850651f, 0.000000f, //
+ -0.238856f, -0.864188f, -0.442863f, //
+ -0.500000f, -0.809017f, -0.309017f, //
+ -0.262866f, -0.951056f, -0.162460f, //
+ -0.850651f, -0.525731f, 0.000000f, //
+ -0.716567f, -0.681718f, -0.147621f, //
+ -0.716567f, -0.681718f, 0.147621f, //
+ -0.525731f, -0.850651f, 0.000000f, //
+ -0.500000f, -0.809017f, 0.309017f, //
+ -0.238856f, -0.864188f, 0.442863f, //
+ -0.262866f, -0.951056f, 0.162460f, //
+ -0.864188f, -0.442863f, 0.238856f, //
+ -0.809017f, -0.309017f, 0.500000f, //
+ -0.688191f, -0.587785f, 0.425325f, //
+ -0.681718f, -0.147621f, 0.716567f, //
+ -0.442863f, -0.238856f, 0.864188f, //
+ -0.587785f, -0.425325f, 0.688191f, //
+ -0.309017f, -0.500000f, 0.809017f, //
+ -0.147621f, -0.716567f, 0.681718f, //
+ -0.425325f, -0.688191f, 0.587785f, //
+ -0.162460f, -0.262866f, 0.951056f, //
+ 0.442863f, -0.238856f, 0.864188f, //
+ 0.162460f, -0.262866f, 0.951056f, //
+ 0.309017f, -0.500000f, 0.809017f, //
+ 0.147621f, -0.716567f, 0.681718f, //
+ 0.000000f, -0.525731f, 0.850651f, //
+ 0.425325f, -0.688191f, 0.587785f, //
+ 0.587785f, -0.425325f, 0.688191f, //
+ 0.688191f, -0.587785f, 0.425325f, //
+ -0.955423f, 0.295242f, 0.000000f, //
+ -0.951056f, 0.162460f, 0.262866f, //
+ -1.000000f, 0.000000f, 0.000000f, //
+ -0.850651f, 0.000000f, 0.525731f, //
+ -0.955423f, -0.295242f, 0.000000f, //
+ -0.951056f, -0.162460f, 0.262866f, //
+ -0.864188f, 0.442863f, -0.238856f, //
+ -0.951056f, 0.162460f, -0.262866f, //
+ -0.809017f, 0.309017f, -0.500000f, //
+ -0.864188f, -0.442863f, -0.238856f, //
+ -0.951056f, -0.162460f, -0.262866f, //
+ -0.809017f, -0.309017f, -0.500000f, //
+ -0.681718f, 0.147621f, -0.716567f, //
+ -0.681718f, -0.147621f, -0.716567f, //
+ -0.850651f, 0.000000f, -0.525731f, //
+ -0.688191f, 0.587785f, -0.425325f, //
+ -0.587785f, 0.425325f, -0.688191f, //
+ -0.425325f, 0.688191f, -0.587785f, //
+ -0.425325f, -0.688191f, -0.587785f, //
+ -0.587785f, -0.425325f, -0.688191f, //
+ -0.688191f, -0.587785f, -0.425325f //
+ };
+
+ public static void getNormalVector(final byte i, final Vector3 store) {
+ final int index = 3 * (i & 0xff);
+ if (index < 0 || index > Md2Normals.NORMALS.length) {
+ store.set(0, 1, 0);
+ } else {
+ store.set(Md2Normals.NORMALS[index + 0], Md2Normals.NORMALS[index + 1], Md2Normals.NORMALS[index + 2]);
+ }
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java
index 6609544..596dead 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/KeyframeController.java
@@ -1,763 +1,763 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util;
-
-import java.io.IOException;
-import java.nio.FloatBuffer;
-import java.util.ArrayList;
-import java.util.logging.Logger;
-
-import com.ardor3d.scenegraph.FloatBufferData;
-import com.ardor3d.scenegraph.IndexBufferData;
-import com.ardor3d.scenegraph.Mesh;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.scenegraph.controller.ComplexSpatialController;
-import com.ardor3d.util.export.InputCapsule;
-import com.ardor3d.util.export.OutputCapsule;
-import com.ardor3d.util.export.Savable;
-import com.ardor3d.util.geom.BufferUtils;
-
-/**
- * TODO: Revisit for better Ardor3D integration.
- *
- * Started Date: Jun 12, 2004 <br>
- *
- *
- * Class can do linear interpolation of a Mesh between units of time. Similar to VertexKeyframeController but
- * interpolates float units of time instead of integer key frames.
- *
- * setSpeed(float) sets a speed relative to the defined speed. For example, the default is 1. A speed of 2 would run
- * twice as fast and a speed of .5 would run half as fast
- *
- * setMinTime(float) and setMaxTime(float) both define the bounds that KeyframeController should follow. It is the
- * programmer's responsibility to make sure that the MinTime and MaxTime are within the span of the defined setKeyframe
- *
- * Controller functions RepeatType and isActive are both defined in their default way for KeyframeController
- *
- * When this controller is saved/loaded to XML format, it assumes that the mesh it morphs is the Mesh it belongs to, so
- * it is recommended to only attach this controller to the Mesh it animates.
- *
- * (Based on work by Jack Lindamood, kevglass (parts), hevee (blend time), Julien Gouesse (port to Ardor3D))
- */
-
-public class KeyframeController<T extends Spatial> extends ComplexSpatialController<T> {
-
- private static final Logger logger = Logger.getLogger(KeyframeController.class.getName());
-
- private static final long serialVersionUID = 1L;
-
- /**
- * An array of PointInTime s that defines the animation
- */
- transient public ArrayList<PointInTime> _keyframes;
-
- /**
- * A special array used with SmoothTransform to store temporary smooth transforms
- */
- transient private ArrayList<PointInTime> _prevKeyframes;
-
- /**
- * The mesh that is actually morphed
- */
- private Mesh _morphMesh;
-
- /**
- * The current time in the animation
- */
- transient private double _curTime;
-
- /**
- * The current frame of the animation
- */
- transient private int _curFrame;
-
- /**
- * The frame of animation we're heading towards
- */
- transient private int _nextFrame;
-
- /**
- * The PointInTime before curTime
- */
- transient private PointInTime _before;
-
- /**
- * The PointInTime after curTime
- */
- transient private PointInTime _after;
-
- /**
- * If true, the animation is moving forward, if false the animation is moving backwards
- */
- transient private boolean _movingForward = true;
-
- /**
- * Used with SmoothTransform to signal it is doing a smooth transform
- */
- transient private boolean _isSmooth;
-
- /**
- * Used with SmoothTransform to signal it is doing a smooth transform
- */
- transient private boolean _interpTex = true;
-
- /**
- * Used with SmoothTransform to hold the new beginning and ending time once the transform is complete
- */
- transient private double _tempNewBeginTime;
-
- transient private double _tempNewEndTime;
-
- /** If true, the model's bounding volume will update every frame. */
- private boolean _updateBounding = true;
-
- /**
- * Default constructor. Speed is 1, MinTime is 0 MaxTime is 0. Both MinTime and MaxTime are automatically adjusted
- * by setKeyframe if the setKeyframe time is less than MinTime or greater than MaxTime. Default RepeatType is WRAP.
- */
- public KeyframeController() {
- setSpeed(1);
- _keyframes = new ArrayList<PointInTime>();
- _curFrame = 0;
- setRepeatType(ComplexSpatialController.RepeatType.WRAP);
- setMinTime(0);
- setMaxTime(0);
- }
-
- public double getCurrentTime() {
- return _curTime;
- }
-
- public int getCurrentFrame() {
- return _curFrame;
- }
-
- /**
- * Gets the current time in the animation
- */
- public double getCurTime() {
- return _curTime;
- }
-
- /**
- * Sets the current time in the animation
- *
- * @param time
- * The time this Controller should continue at
- */
- public void setCurTime(final double time) {
- _curTime = time;
- }
-
- /**
- * Sets the Mesh that will be physically changed by this KeyframeController
- *
- * @param morph
- * The new mesh to morph
- */
- public void setMorphingMesh(final Mesh morph) {
- _morphMesh = morph;
- _keyframes.clear();
- _keyframes.add(new PointInTime(0, null));
- }
-
- public void shallowSetMorphMesh(final Mesh morph) {
- _morphMesh = morph;
- }
-
- /**
- * Tells the controller to change its morphMesh to shape at time seconds. Time must be >=0 and shape must be
- * non-null and shape must have the same number of vertexes as the current shape. If not, then nothing happens. It
- * is also required that setMorphingMesh(TriMesh) is called before setKeyframe. It is assumed that shape.indices ==
- * morphMesh.indices, otherwise morphing may look funny
- *
- * @param time
- * The time for the change
- * @param shape
- * The new shape at that time
- */
- public void setKeyframe(final double time, final Mesh shape) {
- if (_morphMesh == null
- || time < 0
- || shape.getMeshData().getVertexBuffer().capacity() != _morphMesh.getMeshData().getVertexBuffer()
- .capacity()) {
- return;
- }
- for (int i = 0; i < _keyframes.size(); i++) {
- final PointInTime lookingTime = _keyframes.get(i);
- if (lookingTime._time == time) {
- lookingTime._newShape = shape;
- return;
- }
- if (lookingTime._time > time) {
- _keyframes.add(i, new PointInTime(time, shape));
- return;
- }
- }
- _keyframes.add(new PointInTime(time, shape));
- if (time > getMaxTime()) {
- setMaxTime(time);
- }
- if (time < getMinTime()) {
- setMinTime(time);
- }
- }
-
- /**
- * This function will do a smooth translation between a keframe's current look, to the look directly at
- * newTimeToReach. It takes translationLen time (in sec) to do that translation, and once translated will animate
- * like normal between newBeginTime and newEndTime <br>
- * <br>
- * This would be usefull for example when a figure stops running and tries to raise an arm. Instead of "teleporting"
- * to the raise-arm animation begining, a smooth translation can occur.
- *
- * @param newTimeToReach
- * The time to reach.
- * @param translationLen
- * How long it takes
- * @param newBeginTime
- * The new cycle begining time
- * @param newEndTime
- * The new cycle ending time.
- */
- public void setSmoothTranslation(final float newTimeToReach, final float translationLen, final float newBeginTime,
- final float newEndTime) {
- if (!isActive() || _isSmooth) {
- return;
- }
- if (newBeginTime < 0 || newBeginTime > _keyframes.get(_keyframes.size() - 1)._time) {
- KeyframeController.logger.warning("Attempt to set invalid begintime:" + newBeginTime);
- return;
- }
- if (newEndTime < 0 || newEndTime > _keyframes.get(_keyframes.size() - 1)._time) {
- KeyframeController.logger.warning("Attempt to set invalid endtime:" + newEndTime);
- return;
- }
- Mesh begin = null, end = null;
- if (_prevKeyframes == null) {
- _prevKeyframes = new ArrayList<PointInTime>();
- begin = new Mesh();
- end = new Mesh();
- } else {
- begin = _prevKeyframes.get(0)._newShape;
- end = _prevKeyframes.get(1)._newShape;
- _prevKeyframes.clear();
- }
-
- getCurrent(begin);
-
- _curTime = newTimeToReach;
- _curFrame = 0;
- setMinTime(0);
- setMaxTime(_keyframes.get(_keyframes.size() - 1)._time);
- update(0.0d, null);
- getCurrent(end);
-
- swapKeyframeSets();
- _curTime = 0;
- _curFrame = 0;
- setMinTime(0);
- setMaxTime(translationLen);
- setKeyframe(0, begin);
- setKeyframe(translationLen, end);
- _isSmooth = true;
- _tempNewBeginTime = newBeginTime;
- _tempNewEndTime = newEndTime;
- }
-
- /**
- * Swaps prevKeyframes and keyframes
- */
- private void swapKeyframeSets() {
- final ArrayList<PointInTime> temp = _keyframes;
- _keyframes = _prevKeyframes;
- _prevKeyframes = temp;
- }
-
- /**
- * Sets the new animation boundaries for this controller. This will start at newBeginTime and proceed in the
- * direction of newEndTime (either forwards or backwards). If both are the same, then the animation is set to their
- * time and turned off, otherwise the animation is turned on to start the animation acording to the repeat type. If
- * either BeginTime or EndTime are invalid times (less than 0 or greater than the maximum set keyframe time) then a
- * warning is set and nothing happens. <br>
- * It is suggested that this function be called if new animation boundaries need to be set, instead of setMinTime
- * and setMaxTime directly.
- *
- * @param newBeginTime
- * The starting time
- * @param newEndTime
- * The ending time
- */
- public void setNewAnimationTimes(final double newBeginTime, final double newEndTime) {
- if (_isSmooth) {
- return;
- }
- if (newBeginTime < 0 || newBeginTime > _keyframes.get(_keyframes.size() - 1)._time) {
- KeyframeController.logger.warning("Attempt to set invalid begintime:" + newBeginTime);
- return;
- }
- if (newEndTime < 0 || newEndTime > _keyframes.get(_keyframes.size() - 1)._time) {
- KeyframeController.logger.warning("Attempt to set invalid endtime:" + newEndTime);
- return;
- }
- setMinTime(newBeginTime);
- setMaxTime(newEndTime);
- setActive(true);
- if (newBeginTime <= newEndTime) { // Moving forward
- _movingForward = true;
- _curTime = newBeginTime;
- if (newBeginTime == newEndTime) {
- update(0.0d, null);
- setActive(false);
- }
- } else { // Moving backwards
- _movingForward = false;
- _curTime = newEndTime;
- }
- }
-
- /**
- * Saves whatever the current morphMesh looks like into the dataCopy
- *
- * @param dataCopy
- * The copy to save the current mesh into
- */
- private void getCurrent(final Mesh dataCopy) {
- if (_morphMesh.getMeshData().getColorBuffer() != null) {
- FloatBuffer dcColors = dataCopy.getMeshData().getColorBuffer();
- if (dcColors != null) {
- dcColors.clear();
- }
- final FloatBuffer mmColors = _morphMesh.getMeshData().getColorBuffer();
- mmColors.clear();
- if (dcColors == null || dcColors.capacity() != mmColors.capacity()) {
- dcColors = BufferUtils.createFloatBuffer(mmColors.capacity());
- dcColors.clear();
- dataCopy.getMeshData().setColorBuffer(dcColors);
- }
-
- dcColors.put(mmColors);
- dcColors.flip();
- }
- if (_morphMesh.getMeshData().getVertexBuffer() != null) {
- FloatBuffer dcVerts = dataCopy.getMeshData().getVertexBuffer();
- if (dcVerts != null) {
- dcVerts.clear();
- }
- final FloatBuffer mmVerts = _morphMesh.getMeshData().getVertexBuffer();
- mmVerts.clear();
- if (dcVerts == null || dcVerts.capacity() != mmVerts.capacity()) {
- dcVerts = BufferUtils.createFloatBuffer(mmVerts.capacity());
- dcVerts.clear();
- dataCopy.getMeshData().setVertexBuffer(dcVerts);
- }
-
- dcVerts.put(mmVerts);
- dcVerts.flip();
- }
- if (_morphMesh.getMeshData().getNormalBuffer() != null) {
- FloatBuffer dcNorms = dataCopy.getMeshData().getNormalBuffer();
- if (dcNorms != null) {
- dcNorms.clear();
- }
- final FloatBuffer mmNorms = _morphMesh.getMeshData().getNormalBuffer();
- mmNorms.clear();
- if (dcNorms == null || dcNorms.capacity() != mmNorms.capacity()) {
- dcNorms = BufferUtils.createFloatBuffer(mmNorms.capacity());
- dcNorms.clear();
- dataCopy.getMeshData().setNormalBuffer(dcNorms);
- }
-
- dcNorms.put(mmNorms);
- dcNorms.flip();
- }
- if (_morphMesh.getMeshData().getIndices() != null) {
- IndexBufferData<?> dcInds = dataCopy.getMeshData().getIndices();
- if (dcInds != null) {
- dcInds.rewind();
- }
- final IndexBufferData<?> mmInds = _morphMesh.getMeshData().getIndices();
- mmInds.clear();
- if (dcInds == null || dcInds.capacity() != mmInds.capacity() || dcInds.getClass() != mmInds.getClass()) {
- dcInds = BufferUtils.createIndexBufferData(mmInds.capacity(), _morphMesh.getMeshData()
- .getVertexBuffer().capacity() - 1);
- dcInds.clear();
- dataCopy.getMeshData().setIndices(dcInds);
- }
-
- dcInds.put(mmInds);
- dcInds.flip();
- }
- if (_morphMesh.getMeshData().getTextureCoords(0) != null) {
- FloatBuffer dcTexs = dataCopy.getMeshData().getTextureCoords(0).getBuffer();
- if (dcTexs != null) {
- dcTexs.clear();
- }
- final FloatBuffer mmTexs = _morphMesh.getMeshData().getTextureCoords(0).getBuffer();
- mmTexs.clear();
- if (dcTexs == null || dcTexs.capacity() != mmTexs.capacity()) {
- dcTexs = BufferUtils.createFloatBuffer(mmTexs.capacity());
- dcTexs.clear();
- dataCopy.getMeshData().setTextureCoords(new FloatBufferData(dcTexs, 2), 0);
- }
-
- dcTexs.put(mmTexs);
- dcTexs.flip();
- }
- }
-
- /**
- * As defined in Controller
- *
- * @param time
- * as defined in Controller
- */
- @Override
- public void update(final double time, final T caller) {
- if (easyQuit()) {
- return;
- }
- if (_movingForward) {
- _curTime += time * getSpeed();
- } else {
- _curTime -= time * getSpeed();
- }
-
- findFrame();
- _before = _keyframes.get(_curFrame);
- // Change this bit so the next frame we're heading towards isn't always going
- // to be one frame ahead since now we coule be animating from the last to first
- // frames.
- // after = keyframes.get(curFrame + 1));
- _after = _keyframes.get(_nextFrame);
-
- double delta = (_curTime - _before._time) / (_after._time - _before._time);
-
- // If we doing that wrapping bit then delta should be caculated based
- // on the time before the start of the animation we are.
- if (_nextFrame < _curFrame) {
- delta = blendTime - (getMinTime() - _curTime);
- }
-
- final Mesh oldShape = _before._newShape;
- final Mesh newShape = _after._newShape;
-
- final FloatBuffer verts = _morphMesh.getMeshData().getVertexBuffer();
- final FloatBuffer norms = _morphMesh.getMeshData().getNormalBuffer();
- final FloatBuffer texts = _interpTex ? _morphMesh.getMeshData().getTextureCoords(0) != null ? _morphMesh
- .getMeshData().getTextureCoords(0).getBuffer() : null : null;
- final FloatBuffer colors = _morphMesh.getMeshData().getColorBuffer();
-
- final FloatBuffer oldverts = oldShape.getMeshData().getVertexBuffer();
- final FloatBuffer oldnorms = oldShape.getMeshData().getNormalBuffer();
- final FloatBuffer oldtexts = _interpTex ? oldShape.getMeshData().getTextureCoords(0) != null ? oldShape
- .getMeshData().getTextureCoords(0).getBuffer() : null : null;
- final FloatBuffer oldcolors = oldShape.getMeshData().getColorBuffer();
-
- final FloatBuffer newverts = newShape.getMeshData().getVertexBuffer();
- final FloatBuffer newnorms = newShape.getMeshData().getNormalBuffer();
- final FloatBuffer newtexts = _interpTex ? newShape.getMeshData().getTextureCoords(0) != null ? newShape
- .getMeshData().getTextureCoords(0).getBuffer() : null : null;
- final FloatBuffer newcolors = newShape.getMeshData().getColorBuffer();
- if (verts == null || oldverts == null || newverts == null) {
- return;
- }
- final int vertQuantity = verts.capacity() / 3;
- verts.rewind();
- oldverts.rewind();
- newverts.rewind();
-
- if (norms != null) {
- norms.rewind(); // reset to start
- }
- if (oldnorms != null) {
- oldnorms.rewind(); // reset to start
- }
- if (newnorms != null) {
- newnorms.rewind(); // reset to start
- }
-
- if (texts != null) {
- texts.rewind(); // reset to start
- }
- if (oldtexts != null) {
- oldtexts.rewind(); // reset to start
- }
- if (newtexts != null) {
- newtexts.rewind(); // reset to start
- }
-
- if (colors != null) {
- colors.rewind(); // reset to start
- }
- if (oldcolors != null) {
- oldcolors.rewind(); // reset to start
- }
- if (newcolors != null) {
- newcolors.rewind(); // reset to start
- }
-
- for (int i = 0; i < vertQuantity; i++) {
- for (int x = 0; x < 3; x++) {
- verts.put(i * 3 + x, (float) ((1f - delta) * oldverts.get(i * 3 + x) + delta * newverts.get(i * 3 + x)));
- }
-
- if (norms != null && oldnorms != null && newnorms != null) {
- for (int x = 0; x < 3; x++) {
- norms.put(i * 3 + x,
- (float) ((1f - delta) * oldnorms.get(i * 3 + x) + delta * newnorms.get(i * 3 + x)));
- }
- }
-
- if (_interpTex && texts != null && oldtexts != null && newtexts != null) {
- for (int x = 0; x < 2; x++) {
- texts.put(i * 2 + x,
- (float) ((1f - delta) * oldtexts.get(i * 2 + x) + delta * newtexts.get(i * 2 + x)));
- }
- }
-
- if (colors != null && oldcolors != null && newcolors != null) {
- for (int x = 0; x < 4; x++) {
- colors.put(i * 4 + x,
- (float) ((1f - delta) * oldcolors.get(i * 4 + x) + delta * newcolors.get(i * 4 + x)));
- }
- }
- }
-
- if (_updateBounding) {
- _morphMesh.updateModelBound();
- }
- }
-
- /**
- * If both min and max time are equal and the model is already updated, then it's an easy quit, or if it's on CLAMP
- * and I've exceeded my time it's also an easy quit.
- *
- * @return true if update doesn't need to be called, false otherwise
- */
- private boolean easyQuit() {
- if (getMaxTime() == getMinTime() && _curTime != getMinTime()) {
- return true;
- } else if (getRepeatType() == ComplexSpatialController.RepeatType.CLAMP
- && (_curTime > getMaxTime() || _curTime < getMinTime())) {
- return true;
- } else if (_keyframes.size() < 2) {
- return true;
- }
- return false;
- }
-
- /**
- * If true, the model's bounding volume will be updated every frame. If false, it will not.
- *
- * @param update
- * The new update model volume per frame value.
- */
- public void setUpdateBounding(final boolean update) {
- _updateBounding = update;
- }
-
- /**
- * Returns true if the model's bounding volume is being updated every frame.
- *
- * @return True if bounding volume is updating.
- */
- public boolean isUpdateBounding() {
- return _updateBounding;
- }
-
- public void setInterpTex(final boolean interpTex) {
- _interpTex = interpTex;
- }
-
- public boolean isInterpTex() {
- return _interpTex;
- }
-
- private float blendTime = 0;
-
- /**
- * If repeat type RT_WRAP is set, after reaching the last frame of the currently set animation maxTime (see
- * Controller.setMaxTime), there will be an additional blendTime seconds long phase inserted, morphing from the last
- * frame to the first.
- *
- * @param blendTime
- * The blend time to set
- */
- public void setBlendTime(final float blendTime) {
- this.blendTime = blendTime;
- }
-
- /**
- * Gets the currently set blending time for smooth animation transitions
- *
- * @return The current blend time
- * @see #setBlendTime(float blendTime)
- */
- public float getBlendTime() {
- return blendTime;
- }
-
- /**
- * This is used by update(float). It calculates PointInTime before and after as well as makes adjustments on what to
- * do when curTime is beyond the MinTime and MaxTime bounds
- */
- private void findFrame() {
- // If we're in our special wrapping case then just ignore changing
- // frames. Once we get back into the actual series we'll revert back
- // to the normal process
- if (_curTime < getMinTime() && _nextFrame < _curFrame) {
- return;
- }
-
- // Update the rest to maintain our new nextFrame marker as one infront
- // of the curFrame in all cases. The wrap case is where the real work
- // is done.
- if (_curTime > getMaxTime()) {
- if (_isSmooth) {
- swapKeyframeSets();
- _isSmooth = false;
- _curTime = _tempNewBeginTime;
- _curFrame = 0;
- _nextFrame = 1;
- setNewAnimationTimes(_tempNewBeginTime, _tempNewEndTime);
- return;
- }
- if (getRepeatType() == ComplexSpatialController.RepeatType.WRAP) {
- final float delta = blendTime;
- _curTime = getMinTime() - delta;
- _curFrame = Math.min(_curFrame + 1, _keyframes.size() - 1);
-
- for (_nextFrame = 0; _nextFrame < _keyframes.size() - 1; _nextFrame++) {
- if (getMinTime() <= _keyframes.get(_nextFrame)._time) {
- break;
- }
- }
- return;
- } else if (getRepeatType() == ComplexSpatialController.RepeatType.CLAMP) {
- return;
- } else { // Then assume it's RT_CYCLE
- _movingForward = false;
- _curTime = getMaxTime();
- }
- } else if (_curTime < getMinTime()) {
- if (getRepeatType() == ComplexSpatialController.RepeatType.WRAP) {
- _curTime = getMaxTime();
- _curFrame = 0;
- } else if (getRepeatType() == ComplexSpatialController.RepeatType.CLAMP) {
- return;
- } else { // Then assume it's RT_CYCLE
- _movingForward = true;
- _curTime = getMinTime();
- }
- }
-
- _nextFrame = _curFrame + 1;
-
- if (_curTime > _keyframes.get(_curFrame)._time) {
- if (_curTime < _keyframes.get(_curFrame + 1)._time) {
- _nextFrame = _curFrame + 1;
- return;
- }
-
- for (; _curFrame < _keyframes.size() - 1; _curFrame++) {
- if (_curTime <= _keyframes.get(_curFrame + 1)._time) {
- _nextFrame = _curFrame + 1;
- return;
- }
- }
-
- // This -should- be unreachable because of the above
- _curTime = getMinTime();
- _curFrame = 0;
- _nextFrame = _curFrame + 1;
- return;
- }
-
- for (; _curFrame >= 0; _curFrame--) {
- if (_curTime >= _keyframes.get(_curFrame)._time) {
- _nextFrame = _curFrame + 1;
- return;
- }
- }
-
- // This should be unreachable because curTime>=0 and
- // keyframes[0].time=0;
- _curFrame = 0;
- _nextFrame = _curFrame + 1;
- }
-
- /**
- * This class defines a point in time that states _morphShape should look like _newShape at _time seconds
- */
- public static class PointInTime implements Savable {
-
- public Mesh _newShape;
-
- public double _time;
-
- public PointInTime() {}
-
- public PointInTime(final double time, final Mesh shape) {
- this._time = time;
- this._newShape = shape;
- }
-
- public void read(final InputCapsule capsule) throws IOException {
- _time = capsule.readDouble("time", 0);
- _newShape = (Mesh) capsule.readSavable("newShape", null);
- }
-
- public void write(final OutputCapsule capsule) throws IOException {
- capsule.write(_time, "time", 0);
- capsule.write(_newShape, "newShape", null);
- }
-
- public Class<?> getClassTag() {
- return this.getClass();
- }
- }
-
- @SuppressWarnings("unchecked")
- private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- _keyframes = (ArrayList<PointInTime>) in.readObject();
- _movingForward = true;
- }
-
- public Mesh getMorphMesh() {
- return _morphMesh;
- }
-
- @SuppressWarnings("rawtypes")
- @Override
- public Class<? extends KeyframeController> getClassTag() {
- return this.getClass();
- }
-
- @Override
- public void read(final InputCapsule capsule) throws IOException {
- super.read(capsule);
- _updateBounding = capsule.readBoolean("updateBounding", false);
- _morphMesh = (Mesh) capsule.readSavable("morphMesh", null);
- _keyframes = (ArrayList<PointInTime>) capsule.readSavableList("keyframes", new ArrayList<PointInTime>());
- _movingForward = true;
- }
-
- @Override
- public void write(final OutputCapsule capsule) throws IOException {
- super.write(capsule);
- capsule.write(_updateBounding, "updateBounding", true);
- capsule.write(_morphMesh, "morphMesh", null);
- capsule.writeSavableList(_keyframes, "keyframes", new ArrayList<PointInTime>());
- }
-
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util;
+
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.logging.Logger;
+
+import com.ardor3d.scenegraph.FloatBufferData;
+import com.ardor3d.scenegraph.IndexBufferData;
+import com.ardor3d.scenegraph.Mesh;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.scenegraph.controller.ComplexSpatialController;
+import com.ardor3d.util.export.InputCapsule;
+import com.ardor3d.util.export.OutputCapsule;
+import com.ardor3d.util.export.Savable;
+import com.ardor3d.util.geom.BufferUtils;
+
+/**
+ * TODO: Revisit for better Ardor3D integration.
+ *
+ * Started Date: Jun 12, 2004 <br>
+ *
+ *
+ * Class can do linear interpolation of a Mesh between units of time. Similar to VertexKeyframeController but
+ * interpolates float units of time instead of integer key frames.
+ *
+ * setSpeed(float) sets a speed relative to the defined speed. For example, the default is 1. A speed of 2 would run
+ * twice as fast and a speed of .5 would run half as fast
+ *
+ * setMinTime(float) and setMaxTime(float) both define the bounds that KeyframeController should follow. It is the
+ * programmer's responsibility to make sure that the MinTime and MaxTime are within the span of the defined setKeyframe
+ *
+ * Controller functions RepeatType and isActive are both defined in their default way for KeyframeController
+ *
+ * When this controller is saved/loaded to XML format, it assumes that the mesh it morphs is the Mesh it belongs to, so
+ * it is recommended to only attach this controller to the Mesh it animates.
+ *
+ * (Based on work by Jack Lindamood, kevglass (parts), hevee (blend time), Julien Gouesse (port to Ardor3D))
+ */
+
+public class KeyframeController<T extends Spatial> extends ComplexSpatialController<T> {
+
+ private static final Logger logger = Logger.getLogger(KeyframeController.class.getName());
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * An array of PointInTime s that defines the animation
+ */
+ transient public ArrayList<PointInTime> _keyframes;
+
+ /**
+ * A special array used with SmoothTransform to store temporary smooth transforms
+ */
+ transient private ArrayList<PointInTime> _prevKeyframes;
+
+ /**
+ * The mesh that is actually morphed
+ */
+ private Mesh _morphMesh;
+
+ /**
+ * The current time in the animation
+ */
+ transient private double _curTime;
+
+ /**
+ * The current frame of the animation
+ */
+ transient private int _curFrame;
+
+ /**
+ * The frame of animation we're heading towards
+ */
+ transient private int _nextFrame;
+
+ /**
+ * The PointInTime before curTime
+ */
+ transient private PointInTime _before;
+
+ /**
+ * The PointInTime after curTime
+ */
+ transient private PointInTime _after;
+
+ /**
+ * If true, the animation is moving forward, if false the animation is moving backwards
+ */
+ transient private boolean _movingForward = true;
+
+ /**
+ * Used with SmoothTransform to signal it is doing a smooth transform
+ */
+ transient private boolean _isSmooth;
+
+ /**
+ * Used with SmoothTransform to signal it is doing a smooth transform
+ */
+ transient private boolean _interpTex = true;
+
+ /**
+ * Used with SmoothTransform to hold the new beginning and ending time once the transform is complete
+ */
+ transient private double _tempNewBeginTime;
+
+ transient private double _tempNewEndTime;
+
+ /** If true, the model's bounding volume will update every frame. */
+ private boolean _updateBounding = true;
+
+ /**
+ * Default constructor. Speed is 1, MinTime is 0 MaxTime is 0. Both MinTime and MaxTime are automatically adjusted
+ * by setKeyframe if the setKeyframe time is less than MinTime or greater than MaxTime. Default RepeatType is WRAP.
+ */
+ public KeyframeController() {
+ setSpeed(1);
+ _keyframes = new ArrayList<PointInTime>();
+ _curFrame = 0;
+ setRepeatType(ComplexSpatialController.RepeatType.WRAP);
+ setMinTime(0);
+ setMaxTime(0);
+ }
+
+ public double getCurrentTime() {
+ return _curTime;
+ }
+
+ public int getCurrentFrame() {
+ return _curFrame;
+ }
+
+ /**
+ * Gets the current time in the animation
+ */
+ public double getCurTime() {
+ return _curTime;
+ }
+
+ /**
+ * Sets the current time in the animation
+ *
+ * @param time
+ * The time this Controller should continue at
+ */
+ public void setCurTime(final double time) {
+ _curTime = time;
+ }
+
+ /**
+ * Sets the Mesh that will be physically changed by this KeyframeController
+ *
+ * @param morph
+ * The new mesh to morph
+ */
+ public void setMorphingMesh(final Mesh morph) {
+ _morphMesh = morph;
+ _keyframes.clear();
+ _keyframes.add(new PointInTime(0, null));
+ }
+
+ public void shallowSetMorphMesh(final Mesh morph) {
+ _morphMesh = morph;
+ }
+
+ /**
+ * Tells the controller to change its morphMesh to shape at time seconds. Time must be >=0 and shape must be
+ * non-null and shape must have the same number of vertexes as the current shape. If not, then nothing happens. It
+ * is also required that setMorphingMesh(TriMesh) is called before setKeyframe. It is assumed that shape.indices ==
+ * morphMesh.indices, otherwise morphing may look funny
+ *
+ * @param time
+ * The time for the change
+ * @param shape
+ * The new shape at that time
+ */
+ public void setKeyframe(final double time, final Mesh shape) {
+ if (_morphMesh == null
+ || time < 0
+ || shape.getMeshData().getVertexBuffer().capacity() != _morphMesh.getMeshData().getVertexBuffer()
+ .capacity()) {
+ return;
+ }
+ for (int i = 0; i < _keyframes.size(); i++) {
+ final PointInTime lookingTime = _keyframes.get(i);
+ if (lookingTime._time == time) {
+ lookingTime._newShape = shape;
+ return;
+ }
+ if (lookingTime._time > time) {
+ _keyframes.add(i, new PointInTime(time, shape));
+ return;
+ }
+ }
+ _keyframes.add(new PointInTime(time, shape));
+ if (time > getMaxTime()) {
+ setMaxTime(time);
+ }
+ if (time < getMinTime()) {
+ setMinTime(time);
+ }
+ }
+
+ /**
+ * This function will do a smooth translation between a keframe's current look, to the look directly at
+ * newTimeToReach. It takes translationLen time (in sec) to do that translation, and once translated will animate
+ * like normal between newBeginTime and newEndTime <br>
+ * <br>
+ * This would be usefull for example when a figure stops running and tries to raise an arm. Instead of "teleporting"
+ * to the raise-arm animation begining, a smooth translation can occur.
+ *
+ * @param newTimeToReach
+ * The time to reach.
+ * @param translationLen
+ * How long it takes
+ * @param newBeginTime
+ * The new cycle begining time
+ * @param newEndTime
+ * The new cycle ending time.
+ */
+ public void setSmoothTranslation(final float newTimeToReach, final float translationLen, final float newBeginTime,
+ final float newEndTime) {
+ if (!isActive() || _isSmooth) {
+ return;
+ }
+ if (newBeginTime < 0 || newBeginTime > _keyframes.get(_keyframes.size() - 1)._time) {
+ KeyframeController.logger.warning("Attempt to set invalid begintime:" + newBeginTime);
+ return;
+ }
+ if (newEndTime < 0 || newEndTime > _keyframes.get(_keyframes.size() - 1)._time) {
+ KeyframeController.logger.warning("Attempt to set invalid endtime:" + newEndTime);
+ return;
+ }
+ Mesh begin = null, end = null;
+ if (_prevKeyframes == null) {
+ _prevKeyframes = new ArrayList<PointInTime>();
+ begin = new Mesh();
+ end = new Mesh();
+ } else {
+ begin = _prevKeyframes.get(0)._newShape;
+ end = _prevKeyframes.get(1)._newShape;
+ _prevKeyframes.clear();
+ }
+
+ getCurrent(begin);
+
+ _curTime = newTimeToReach;
+ _curFrame = 0;
+ setMinTime(0);
+ setMaxTime(_keyframes.get(_keyframes.size() - 1)._time);
+ update(0.0d, null);
+ getCurrent(end);
+
+ swapKeyframeSets();
+ _curTime = 0;
+ _curFrame = 0;
+ setMinTime(0);
+ setMaxTime(translationLen);
+ setKeyframe(0, begin);
+ setKeyframe(translationLen, end);
+ _isSmooth = true;
+ _tempNewBeginTime = newBeginTime;
+ _tempNewEndTime = newEndTime;
+ }
+
+ /**
+ * Swaps prevKeyframes and keyframes
+ */
+ private void swapKeyframeSets() {
+ final ArrayList<PointInTime> temp = _keyframes;
+ _keyframes = _prevKeyframes;
+ _prevKeyframes = temp;
+ }
+
+ /**
+ * Sets the new animation boundaries for this controller. This will start at newBeginTime and proceed in the
+ * direction of newEndTime (either forwards or backwards). If both are the same, then the animation is set to their
+ * time and turned off, otherwise the animation is turned on to start the animation acording to the repeat type. If
+ * either BeginTime or EndTime are invalid times (less than 0 or greater than the maximum set keyframe time) then a
+ * warning is set and nothing happens. <br>
+ * It is suggested that this function be called if new animation boundaries need to be set, instead of setMinTime
+ * and setMaxTime directly.
+ *
+ * @param newBeginTime
+ * The starting time
+ * @param newEndTime
+ * The ending time
+ */
+ public void setNewAnimationTimes(final double newBeginTime, final double newEndTime) {
+ if (_isSmooth) {
+ return;
+ }
+ if (newBeginTime < 0 || newBeginTime > _keyframes.get(_keyframes.size() - 1)._time) {
+ KeyframeController.logger.warning("Attempt to set invalid begintime:" + newBeginTime);
+ return;
+ }
+ if (newEndTime < 0 || newEndTime > _keyframes.get(_keyframes.size() - 1)._time) {
+ KeyframeController.logger.warning("Attempt to set invalid endtime:" + newEndTime);
+ return;
+ }
+ setMinTime(newBeginTime);
+ setMaxTime(newEndTime);
+ setActive(true);
+ if (newBeginTime <= newEndTime) { // Moving forward
+ _movingForward = true;
+ _curTime = newBeginTime;
+ if (newBeginTime == newEndTime) {
+ update(0.0d, null);
+ setActive(false);
+ }
+ } else { // Moving backwards
+ _movingForward = false;
+ _curTime = newEndTime;
+ }
+ }
+
+ /**
+ * Saves whatever the current morphMesh looks like into the dataCopy
+ *
+ * @param dataCopy
+ * The copy to save the current mesh into
+ */
+ private void getCurrent(final Mesh dataCopy) {
+ if (_morphMesh.getMeshData().getColorBuffer() != null) {
+ FloatBuffer dcColors = dataCopy.getMeshData().getColorBuffer();
+ if (dcColors != null) {
+ dcColors.clear();
+ }
+ final FloatBuffer mmColors = _morphMesh.getMeshData().getColorBuffer();
+ mmColors.clear();
+ if (dcColors == null || dcColors.capacity() != mmColors.capacity()) {
+ dcColors = BufferUtils.createFloatBuffer(mmColors.capacity());
+ dcColors.clear();
+ dataCopy.getMeshData().setColorBuffer(dcColors);
+ }
+
+ dcColors.put(mmColors);
+ dcColors.flip();
+ }
+ if (_morphMesh.getMeshData().getVertexBuffer() != null) {
+ FloatBuffer dcVerts = dataCopy.getMeshData().getVertexBuffer();
+ if (dcVerts != null) {
+ dcVerts.clear();
+ }
+ final FloatBuffer mmVerts = _morphMesh.getMeshData().getVertexBuffer();
+ mmVerts.clear();
+ if (dcVerts == null || dcVerts.capacity() != mmVerts.capacity()) {
+ dcVerts = BufferUtils.createFloatBuffer(mmVerts.capacity());
+ dcVerts.clear();
+ dataCopy.getMeshData().setVertexBuffer(dcVerts);
+ }
+
+ dcVerts.put(mmVerts);
+ dcVerts.flip();
+ }
+ if (_morphMesh.getMeshData().getNormalBuffer() != null) {
+ FloatBuffer dcNorms = dataCopy.getMeshData().getNormalBuffer();
+ if (dcNorms != null) {
+ dcNorms.clear();
+ }
+ final FloatBuffer mmNorms = _morphMesh.getMeshData().getNormalBuffer();
+ mmNorms.clear();
+ if (dcNorms == null || dcNorms.capacity() != mmNorms.capacity()) {
+ dcNorms = BufferUtils.createFloatBuffer(mmNorms.capacity());
+ dcNorms.clear();
+ dataCopy.getMeshData().setNormalBuffer(dcNorms);
+ }
+
+ dcNorms.put(mmNorms);
+ dcNorms.flip();
+ }
+ if (_morphMesh.getMeshData().getIndices() != null) {
+ IndexBufferData<?> dcInds = dataCopy.getMeshData().getIndices();
+ if (dcInds != null) {
+ dcInds.rewind();
+ }
+ final IndexBufferData<?> mmInds = _morphMesh.getMeshData().getIndices();
+ mmInds.clear();
+ if (dcInds == null || dcInds.capacity() != mmInds.capacity() || dcInds.getClass() != mmInds.getClass()) {
+ dcInds = BufferUtils.createIndexBufferData(mmInds.capacity(), _morphMesh.getMeshData()
+ .getVertexBuffer().capacity() - 1);
+ dcInds.clear();
+ dataCopy.getMeshData().setIndices(dcInds);
+ }
+
+ dcInds.put(mmInds);
+ dcInds.flip();
+ }
+ if (_morphMesh.getMeshData().getTextureCoords(0) != null) {
+ FloatBuffer dcTexs = dataCopy.getMeshData().getTextureCoords(0).getBuffer();
+ if (dcTexs != null) {
+ dcTexs.clear();
+ }
+ final FloatBuffer mmTexs = _morphMesh.getMeshData().getTextureCoords(0).getBuffer();
+ mmTexs.clear();
+ if (dcTexs == null || dcTexs.capacity() != mmTexs.capacity()) {
+ dcTexs = BufferUtils.createFloatBuffer(mmTexs.capacity());
+ dcTexs.clear();
+ dataCopy.getMeshData().setTextureCoords(new FloatBufferData(dcTexs, 2), 0);
+ }
+
+ dcTexs.put(mmTexs);
+ dcTexs.flip();
+ }
+ }
+
+ /**
+ * As defined in Controller
+ *
+ * @param time
+ * as defined in Controller
+ */
+ @Override
+ public void update(final double time, final T caller) {
+ if (easyQuit()) {
+ return;
+ }
+ if (_movingForward) {
+ _curTime += time * getSpeed();
+ } else {
+ _curTime -= time * getSpeed();
+ }
+
+ findFrame();
+ _before = _keyframes.get(_curFrame);
+ // Change this bit so the next frame we're heading towards isn't always going
+ // to be one frame ahead since now we coule be animating from the last to first
+ // frames.
+ // after = keyframes.get(curFrame + 1));
+ _after = _keyframes.get(_nextFrame);
+
+ double delta = (_curTime - _before._time) / (_after._time - _before._time);
+
+ // If we doing that wrapping bit then delta should be caculated based
+ // on the time before the start of the animation we are.
+ if (_nextFrame < _curFrame) {
+ delta = blendTime - (getMinTime() - _curTime);
+ }
+
+ final Mesh oldShape = _before._newShape;
+ final Mesh newShape = _after._newShape;
+
+ final FloatBuffer verts = _morphMesh.getMeshData().getVertexBuffer();
+ final FloatBuffer norms = _morphMesh.getMeshData().getNormalBuffer();
+ final FloatBuffer texts = _interpTex ? _morphMesh.getMeshData().getTextureCoords(0) != null ? _morphMesh
+ .getMeshData().getTextureCoords(0).getBuffer() : null : null;
+ final FloatBuffer colors = _morphMesh.getMeshData().getColorBuffer();
+
+ final FloatBuffer oldverts = oldShape.getMeshData().getVertexBuffer();
+ final FloatBuffer oldnorms = oldShape.getMeshData().getNormalBuffer();
+ final FloatBuffer oldtexts = _interpTex ? oldShape.getMeshData().getTextureCoords(0) != null ? oldShape
+ .getMeshData().getTextureCoords(0).getBuffer() : null : null;
+ final FloatBuffer oldcolors = oldShape.getMeshData().getColorBuffer();
+
+ final FloatBuffer newverts = newShape.getMeshData().getVertexBuffer();
+ final FloatBuffer newnorms = newShape.getMeshData().getNormalBuffer();
+ final FloatBuffer newtexts = _interpTex ? newShape.getMeshData().getTextureCoords(0) != null ? newShape
+ .getMeshData().getTextureCoords(0).getBuffer() : null : null;
+ final FloatBuffer newcolors = newShape.getMeshData().getColorBuffer();
+ if (verts == null || oldverts == null || newverts == null) {
+ return;
+ }
+ final int vertQuantity = verts.capacity() / 3;
+ verts.rewind();
+ oldverts.rewind();
+ newverts.rewind();
+
+ if (norms != null) {
+ norms.rewind(); // reset to start
+ }
+ if (oldnorms != null) {
+ oldnorms.rewind(); // reset to start
+ }
+ if (newnorms != null) {
+ newnorms.rewind(); // reset to start
+ }
+
+ if (texts != null) {
+ texts.rewind(); // reset to start
+ }
+ if (oldtexts != null) {
+ oldtexts.rewind(); // reset to start
+ }
+ if (newtexts != null) {
+ newtexts.rewind(); // reset to start
+ }
+
+ if (colors != null) {
+ colors.rewind(); // reset to start
+ }
+ if (oldcolors != null) {
+ oldcolors.rewind(); // reset to start
+ }
+ if (newcolors != null) {
+ newcolors.rewind(); // reset to start
+ }
+
+ for (int i = 0; i < vertQuantity; i++) {
+ for (int x = 0; x < 3; x++) {
+ verts.put(i * 3 + x, (float) ((1f - delta) * oldverts.get(i * 3 + x) + delta * newverts.get(i * 3 + x)));
+ }
+
+ if (norms != null && oldnorms != null && newnorms != null) {
+ for (int x = 0; x < 3; x++) {
+ norms.put(i * 3 + x,
+ (float) ((1f - delta) * oldnorms.get(i * 3 + x) + delta * newnorms.get(i * 3 + x)));
+ }
+ }
+
+ if (_interpTex && texts != null && oldtexts != null && newtexts != null) {
+ for (int x = 0; x < 2; x++) {
+ texts.put(i * 2 + x,
+ (float) ((1f - delta) * oldtexts.get(i * 2 + x) + delta * newtexts.get(i * 2 + x)));
+ }
+ }
+
+ if (colors != null && oldcolors != null && newcolors != null) {
+ for (int x = 0; x < 4; x++) {
+ colors.put(i * 4 + x,
+ (float) ((1f - delta) * oldcolors.get(i * 4 + x) + delta * newcolors.get(i * 4 + x)));
+ }
+ }
+ }
+
+ if (_updateBounding) {
+ _morphMesh.updateModelBound();
+ }
+ }
+
+ /**
+ * If both min and max time are equal and the model is already updated, then it's an easy quit, or if it's on CLAMP
+ * and I've exceeded my time it's also an easy quit.
+ *
+ * @return true if update doesn't need to be called, false otherwise
+ */
+ private boolean easyQuit() {
+ if (getMaxTime() == getMinTime() && _curTime != getMinTime()) {
+ return true;
+ } else if (getRepeatType() == ComplexSpatialController.RepeatType.CLAMP
+ && (_curTime > getMaxTime() || _curTime < getMinTime())) {
+ return true;
+ } else if (_keyframes.size() < 2) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * If true, the model's bounding volume will be updated every frame. If false, it will not.
+ *
+ * @param update
+ * The new update model volume per frame value.
+ */
+ public void setUpdateBounding(final boolean update) {
+ _updateBounding = update;
+ }
+
+ /**
+ * Returns true if the model's bounding volume is being updated every frame.
+ *
+ * @return True if bounding volume is updating.
+ */
+ public boolean isUpdateBounding() {
+ return _updateBounding;
+ }
+
+ public void setInterpTex(final boolean interpTex) {
+ _interpTex = interpTex;
+ }
+
+ public boolean isInterpTex() {
+ return _interpTex;
+ }
+
+ private float blendTime = 0;
+
+ /**
+ * If repeat type RT_WRAP is set, after reaching the last frame of the currently set animation maxTime (see
+ * Controller.setMaxTime), there will be an additional blendTime seconds long phase inserted, morphing from the last
+ * frame to the first.
+ *
+ * @param blendTime
+ * The blend time to set
+ */
+ public void setBlendTime(final float blendTime) {
+ this.blendTime = blendTime;
+ }
+
+ /**
+ * Gets the currently set blending time for smooth animation transitions
+ *
+ * @return The current blend time
+ * @see #setBlendTime(float blendTime)
+ */
+ public float getBlendTime() {
+ return blendTime;
+ }
+
+ /**
+ * This is used by update(float). It calculates PointInTime before and after as well as makes adjustments on what to
+ * do when curTime is beyond the MinTime and MaxTime bounds
+ */
+ private void findFrame() {
+ // If we're in our special wrapping case then just ignore changing
+ // frames. Once we get back into the actual series we'll revert back
+ // to the normal process
+ if (_curTime < getMinTime() && _nextFrame < _curFrame) {
+ return;
+ }
+
+ // Update the rest to maintain our new nextFrame marker as one infront
+ // of the curFrame in all cases. The wrap case is where the real work
+ // is done.
+ if (_curTime > getMaxTime()) {
+ if (_isSmooth) {
+ swapKeyframeSets();
+ _isSmooth = false;
+ _curTime = _tempNewBeginTime;
+ _curFrame = 0;
+ _nextFrame = 1;
+ setNewAnimationTimes(_tempNewBeginTime, _tempNewEndTime);
+ return;
+ }
+ if (getRepeatType() == ComplexSpatialController.RepeatType.WRAP) {
+ final float delta = blendTime;
+ _curTime = getMinTime() - delta;
+ _curFrame = Math.min(_curFrame + 1, _keyframes.size() - 1);
+
+ for (_nextFrame = 0; _nextFrame < _keyframes.size() - 1; _nextFrame++) {
+ if (getMinTime() <= _keyframes.get(_nextFrame)._time) {
+ break;
+ }
+ }
+ return;
+ } else if (getRepeatType() == ComplexSpatialController.RepeatType.CLAMP) {
+ return;
+ } else { // Then assume it's RT_CYCLE
+ _movingForward = false;
+ _curTime = getMaxTime();
+ }
+ } else if (_curTime < getMinTime()) {
+ if (getRepeatType() == ComplexSpatialController.RepeatType.WRAP) {
+ _curTime = getMaxTime();
+ _curFrame = 0;
+ } else if (getRepeatType() == ComplexSpatialController.RepeatType.CLAMP) {
+ return;
+ } else { // Then assume it's RT_CYCLE
+ _movingForward = true;
+ _curTime = getMinTime();
+ }
+ }
+
+ _nextFrame = _curFrame + 1;
+
+ if (_curTime > _keyframes.get(_curFrame)._time) {
+ if (_curTime < _keyframes.get(_curFrame + 1)._time) {
+ _nextFrame = _curFrame + 1;
+ return;
+ }
+
+ for (; _curFrame < _keyframes.size() - 1; _curFrame++) {
+ if (_curTime <= _keyframes.get(_curFrame + 1)._time) {
+ _nextFrame = _curFrame + 1;
+ return;
+ }
+ }
+
+ // This -should- be unreachable because of the above
+ _curTime = getMinTime();
+ _curFrame = 0;
+ _nextFrame = _curFrame + 1;
+ return;
+ }
+
+ for (; _curFrame >= 0; _curFrame--) {
+ if (_curTime >= _keyframes.get(_curFrame)._time) {
+ _nextFrame = _curFrame + 1;
+ return;
+ }
+ }
+
+ // This should be unreachable because curTime>=0 and
+ // keyframes[0].time=0;
+ _curFrame = 0;
+ _nextFrame = _curFrame + 1;
+ }
+
+ /**
+ * This class defines a point in time that states _morphShape should look like _newShape at _time seconds
+ */
+ public static class PointInTime implements Savable {
+
+ public Mesh _newShape;
+
+ public double _time;
+
+ public PointInTime() {}
+
+ public PointInTime(final double time, final Mesh shape) {
+ this._time = time;
+ this._newShape = shape;
+ }
+
+ public void read(final InputCapsule capsule) throws IOException {
+ _time = capsule.readDouble("time", 0);
+ _newShape = (Mesh) capsule.readSavable("newShape", null);
+ }
+
+ public void write(final OutputCapsule capsule) throws IOException {
+ capsule.write(_time, "time", 0);
+ capsule.write(_newShape, "newShape", null);
+ }
+
+ public Class<?> getClassTag() {
+ return this.getClass();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ _keyframes = (ArrayList<PointInTime>) in.readObject();
+ _movingForward = true;
+ }
+
+ public Mesh getMorphMesh() {
+ return _morphMesh;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Class<? extends KeyframeController> getClassTag() {
+ return this.getClass();
+ }
+
+ @Override
+ public void read(final InputCapsule capsule) throws IOException {
+ super.read(capsule);
+ _updateBounding = capsule.readBoolean("updateBounding", false);
+ _morphMesh = (Mesh) capsule.readSavable("morphMesh", null);
+ _keyframes = (ArrayList<PointInTime>) capsule.readSavableList("keyframes", new ArrayList<PointInTime>());
+ _movingForward = true;
+ }
+
+ @Override
+ public void write(final OutputCapsule capsule) throws IOException {
+ super.write(capsule);
+ capsule.write(_updateBounding, "updateBounding", true);
+ capsule.write(_morphMesh, "morphMesh", null);
+ capsule.writeSavableList(_keyframes, "keyframes", new ArrayList<PointInTime>());
+ }
+
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java
index 3ce40c2..ee49d56 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvEdgeInfo.java
@@ -1,36 +1,36 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-/**
- * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
- */
-final class NvEdgeInfo {
- long _refCount;
- NvFaceInfo _face0, _face1;
- int _v0, _v1;
- NvEdgeInfo _nextV0, _nextV1;
-
- // constructor puts 1 ref on us
- NvEdgeInfo(final int v0, final int v1) {
- _v0 = v0;
- _v1 = v1;
- _face0 = null;
- _face1 = null;
- _nextV0 = null;
- _nextV1 = null;
-
- // we will appear in 2 lists. this is a good
- // way to make sure we delete it the second time
- // we hit it in the edge infos
- _refCount = 2;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+/**
+ * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
+ */
+final class NvEdgeInfo {
+ long _refCount;
+ NvFaceInfo _face0, _face1;
+ int _v0, _v1;
+ NvEdgeInfo _nextV0, _nextV1;
+
+ // constructor puts 1 ref on us
+ NvEdgeInfo(final int v0, final int v1) {
+ _v0 = v0;
+ _v1 = v1;
+ _face0 = null;
+ _face1 = null;
+ _nextV0 = null;
+ _nextV1 = null;
+
+ // we will appear in 2 lists. this is a good
+ // way to make sure we delete it the second time
+ // we hit it in the edge infos
+ _refCount = 2;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java
index ae51a6a..688c73d 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvFaceInfo.java
@@ -1,45 +1,45 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-/**
- * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
- */
-final class NvFaceInfo {
- int _v0, _v1, _v2;
- int _stripId; // real strip Id
- int _testStripId; // strip Id in an experiment
- int _experimentId; // in what experiment was it given an experiment Id?
- boolean _isFake; // if true, will be deleted when the strip it's in is deleted
-
- NvFaceInfo(final int v0, final int v1, final int v2) {
- this(v0, v1, v2, false);
- }
-
- NvFaceInfo(final int v0, final int v1, final int v2, final boolean bIsFake) {
- _v0 = v0;
- _v1 = v1;
- _v2 = v2;
- _stripId = -1;
- _testStripId = -1;
- _experimentId = -1;
- _isFake = bIsFake;
- }
-
- /**
- * Copies only v0, v1 and v2
- *
- * @param source
- */
- public NvFaceInfo(final NvFaceInfo source) {
- this(source._v0, source._v1, source._v2);
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+/**
+ * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
+ */
+final class NvFaceInfo {
+ int _v0, _v1, _v2;
+ int _stripId; // real strip Id
+ int _testStripId; // strip Id in an experiment
+ int _experimentId; // in what experiment was it given an experiment Id?
+ boolean _isFake; // if true, will be deleted when the strip it's in is deleted
+
+ NvFaceInfo(final int v0, final int v1, final int v2) {
+ this(v0, v1, v2, false);
+ }
+
+ NvFaceInfo(final int v0, final int v1, final int v2, final boolean bIsFake) {
+ _v0 = v0;
+ _v1 = v1;
+ _v2 = v2;
+ _stripId = -1;
+ _testStripId = -1;
+ _experimentId = -1;
+ _isFake = bIsFake;
+ }
+
+ /**
+ * Copies only v0, v1 and v2
+ *
+ * @param source
+ */
+ public NvFaceInfo(final NvFaceInfo source) {
+ this(source._v0, source._v1, source._v2);
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java
index 5808e5d..2cf0e18 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripInfo.java
@@ -1,323 +1,323 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-import java.util.List;
-
-import com.google.common.collect.Lists;
-
-final class NvStripInfo {
- NvStripStartInfo _startInfo;
- List<NvFaceInfo> _faces = Lists.newArrayList();
- int _stripId;
- int _experimentId;
-
- boolean _visited;
-
- int _numDegenerates;
-
- // A little information about the creation of the triangle strips
- NvStripInfo(final NvStripStartInfo startInfo, final int stripId) {
- this(startInfo, stripId, -1);
- }
-
- NvStripInfo(final NvStripStartInfo startInfo, final int stripId, final int experimentId) {
- _startInfo = startInfo;
- _stripId = stripId;
- _experimentId = experimentId;
- _visited = false;
- _numDegenerates = 0;
- }
-
- /**
- * @return true if the experiment id is >= 0
- */
- final boolean isExperiment() {
- return _experimentId >= 0;
- }
-
- /**
- * @param faceInfo
- * @return
- */
- final boolean IsInStrip(final NvFaceInfo faceInfo) {
- if (faceInfo == null) {
- return false;
- }
-
- return _experimentId >= 0 ? faceInfo._testStripId == _stripId : faceInfo._stripId == _stripId;
- }
-
- /**
- *
- * @param faceInfo
- * @param edgeInfos
- * @return true if the input face and the current strip share an edge
- */
- boolean sharesEdge(final NvFaceInfo faceInfo, final List<NvEdgeInfo> edgeInfos) {
- // check v0->v1 edge
- NvEdgeInfo currEdge = NvStripifier.findEdgeInfo(edgeInfos, faceInfo._v0, faceInfo._v1);
-
- if (IsInStrip(currEdge._face0) || IsInStrip(currEdge._face1)) {
- return true;
- }
-
- // check v1->v2 edge
- currEdge = NvStripifier.findEdgeInfo(edgeInfos, faceInfo._v1, faceInfo._v2);
-
- if (IsInStrip(currEdge._face0) || IsInStrip(currEdge._face1)) {
- return true;
- }
-
- // check v2->v0 edge
- currEdge = NvStripifier.findEdgeInfo(edgeInfos, faceInfo._v2, faceInfo._v0);
-
- if (IsInStrip(currEdge._face0) || IsInStrip(currEdge._face1)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * take the given forward and backward strips and combine them together
- *
- * @param forward
- * @param backward
- */
- void combine(final List<NvFaceInfo> forward, final List<NvFaceInfo> backward) {
- // add backward faces
- int numFaces = backward.size();
- for (int i = numFaces - 1; i >= 0; i--) {
- _faces.add(backward.get(i));
- }
-
- // add forward faces
- numFaces = forward.size();
- for (int i = 0; i < numFaces; i++) {
- _faces.add(forward.get(i));
- }
- }
-
- /**
- * @param faceVec
- * @param face
- * @return true if the face is "unique", i.e. has a vertex which doesn't exist in the faceVec
- */
- boolean unique(final List<NvFaceInfo> faceVec, final NvFaceInfo face) {
- boolean bv0, bv1, bv2; // bools to indicate whether a vertex is in the faceVec or not
- bv0 = bv1 = bv2 = false;
-
- for (int i = 0; i < faceVec.size(); i++) {
- if (!bv0) {
- if (faceVec.get(i)._v0 == face._v0 || faceVec.get(i)._v1 == face._v0 || faceVec.get(i)._v2 == face._v0) {
- bv0 = true;
- }
- }
-
- if (!bv1) {
- if (faceVec.get(i)._v0 == face._v1 || faceVec.get(i)._v1 == face._v1 || faceVec.get(i)._v2 == face._v1) {
- bv1 = true;
- }
- }
-
- if (!bv2) {
- if (faceVec.get(i)._v0 == face._v2 || faceVec.get(i)._v1 == face._v2 || faceVec.get(i)._v2 == face._v2) {
- bv2 = true;
- }
- }
-
- // the face is not unique, all its vertices exist in the face vector
- if (bv0 && bv1 && bv2) {
- return false;
- }
- }
-
- // if we get out here, it's unique
- return true;
- }
-
- /**
- * If either the faceInfo has a real strip index because it is already assign to a committed strip OR it is assigned
- * in an experiment and the experiment index is the one we are building for, then it is marked and unavailable
- */
- boolean isMarked(final NvFaceInfo faceInfo) {
- return faceInfo._stripId >= 0 || isExperiment() && faceInfo._experimentId == _experimentId;
- }
-
- /**
- * Marks the face with the current strip ID
- *
- * @param faceInfo
- */
- void markTriangle(final NvFaceInfo faceInfo) {
- assert !isMarked(faceInfo);
- if (isExperiment()) {
- faceInfo._experimentId = _experimentId;
- faceInfo._testStripId = _stripId;
- } else {
- faceInfo._experimentId = -1;
- faceInfo._stripId = _stripId;
- }
- }
-
- /**
- * Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
- *
- * @param edgeInfos
- * @param faceInfos
- */
- void build(final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> faceInfos) {
- // used in building the strips forward and backward
- final List<Integer> scratchIndices = Lists.newArrayList();
-
- // build forward... start with the initial face
- final List<NvFaceInfo> forwardFaces = Lists.newArrayList();
- final List<NvFaceInfo> backwardFaces = Lists.newArrayList();
- forwardFaces.add(_startInfo._startFace);
-
- markTriangle(_startInfo._startFace);
-
- final int v0 = _startInfo._toV1 ? _startInfo._startEdge._v0 : _startInfo._startEdge._v1;
- final int v1 = _startInfo._toV1 ? _startInfo._startEdge._v1 : _startInfo._startEdge._v0;
-
- // easiest way to get v2 is to use this function which requires the
- // other indices to already be in the list.
- scratchIndices.add(v0);
- scratchIndices.add(v1);
- final int v2 = NvStripifier.getNextIndex(scratchIndices, _startInfo._startFace);
- scratchIndices.add(v2);
-
- //
- // build the forward list
- //
- int nv0 = v1;
- int nv1 = v2;
-
- NvFaceInfo nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, _startInfo._startFace);
- while (nextFace != null && !isMarked(nextFace)) {
- // check to see if this next face is going to cause us to die soon
- int testnv0 = nv1;
- final int testnv1 = NvStripifier.getNextIndex(scratchIndices, nextFace);
-
- final NvFaceInfo nextNextFace = NvStripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);
-
- if (nextNextFace == null || isMarked(nextNextFace)) {
- // uh, oh, we're following a dead end, try swapping
- final NvFaceInfo testNextFace = NvStripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
-
- if (testNextFace != null && !isMarked(testNextFace)) {
- // we only swap if it buys us something
-
- // add a "fake" degenerate face
- final NvFaceInfo tempFace = new NvFaceInfo(nv0, nv1, nv0, true);
-
- forwardFaces.add(tempFace);
- markTriangle(tempFace);
-
- scratchIndices.add(nv0);
- testnv0 = nv0;
-
- ++_numDegenerates;
- }
-
- }
-
- // add this to the strip
- forwardFaces.add(nextFace);
-
- markTriangle(nextFace);
-
- // add the index
- // nv0 = nv1;
- // nv1 = NvStripifier.GetNextIndex(scratchIndices, nextFace);
- scratchIndices.add(testnv1);
-
- // and get the next face
- nv0 = testnv0;
- nv1 = testnv1;
-
- nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
-
- }
-
- // tempAllFaces is going to be forwardFaces + backwardFaces
- // it's used for Unique()
- final List<NvFaceInfo> tempAllFaces = Lists.newArrayList();
- for (int i = 0; i < forwardFaces.size(); i++) {
- tempAllFaces.add(forwardFaces.get(i));
- }
-
- //
- // reset the indices for building the strip backwards and do so
- //
- scratchIndices.clear();
- scratchIndices.add(v2);
- scratchIndices.add(v1);
- scratchIndices.add(v0);
- nv0 = v1;
- nv1 = v0;
- nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, _startInfo._startFace);
- while (nextFace != null && !isMarked(nextFace)) {
- // this tests to see if a face is "unique", meaning that its vertices aren't already in the list
- // so, strips which "wrap-around" are not allowed
- if (!unique(tempAllFaces, nextFace)) {
- break;
- }
-
- // check to see if this next face is going to cause us to die soon
- int testnv0 = nv1;
- final int testnv1 = NvStripifier.getNextIndex(scratchIndices, nextFace);
-
- final NvFaceInfo nextNextFace = NvStripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);
-
- if (nextNextFace == null || isMarked(nextNextFace)) {
- // uh, oh, we're following a dead end, try swapping
- final NvFaceInfo testNextFace = NvStripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
- if (testNextFace != null && !isMarked(testNextFace)) {
- // we only swap if it buys us something
-
- // add a "fake" degenerate face
- final NvFaceInfo tempFace = new NvFaceInfo(nv0, nv1, nv0, true);
-
- backwardFaces.add(tempFace);
- markTriangle(tempFace);
- scratchIndices.add(nv0);
- testnv0 = nv0;
-
- ++_numDegenerates;
- }
-
- }
-
- // add this to the strip
- backwardFaces.add(nextFace);
-
- // this is just so Unique() will work
- tempAllFaces.add(nextFace);
-
- markTriangle(nextFace);
-
- // add the index
- // nv0 = nv1;
- // nv1 = NvStripifier.GetNextIndex(scratchIndices, nextFace);
- scratchIndices.add(testnv1);
-
- // and get the next face
- nv0 = testnv0;
- nv1 = testnv1;
- nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
- }
-
- // Combine the forward and backwards stripification lists and put into our own face vector
- combine(forwardFaces, backwardFaces);
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+final class NvStripInfo {
+ NvStripStartInfo _startInfo;
+ List<NvFaceInfo> _faces = Lists.newArrayList();
+ int _stripId;
+ int _experimentId;
+
+ boolean _visited;
+
+ int _numDegenerates;
+
+ // A little information about the creation of the triangle strips
+ NvStripInfo(final NvStripStartInfo startInfo, final int stripId) {
+ this(startInfo, stripId, -1);
+ }
+
+ NvStripInfo(final NvStripStartInfo startInfo, final int stripId, final int experimentId) {
+ _startInfo = startInfo;
+ _stripId = stripId;
+ _experimentId = experimentId;
+ _visited = false;
+ _numDegenerates = 0;
+ }
+
+ /**
+ * @return true if the experiment id is >= 0
+ */
+ final boolean isExperiment() {
+ return _experimentId >= 0;
+ }
+
+ /**
+ * @param faceInfo
+ * @return
+ */
+ final boolean IsInStrip(final NvFaceInfo faceInfo) {
+ if (faceInfo == null) {
+ return false;
+ }
+
+ return _experimentId >= 0 ? faceInfo._testStripId == _stripId : faceInfo._stripId == _stripId;
+ }
+
+ /**
+ *
+ * @param faceInfo
+ * @param edgeInfos
+ * @return true if the input face and the current strip share an edge
+ */
+ boolean sharesEdge(final NvFaceInfo faceInfo, final List<NvEdgeInfo> edgeInfos) {
+ // check v0->v1 edge
+ NvEdgeInfo currEdge = NvStripifier.findEdgeInfo(edgeInfos, faceInfo._v0, faceInfo._v1);
+
+ if (IsInStrip(currEdge._face0) || IsInStrip(currEdge._face1)) {
+ return true;
+ }
+
+ // check v1->v2 edge
+ currEdge = NvStripifier.findEdgeInfo(edgeInfos, faceInfo._v1, faceInfo._v2);
+
+ if (IsInStrip(currEdge._face0) || IsInStrip(currEdge._face1)) {
+ return true;
+ }
+
+ // check v2->v0 edge
+ currEdge = NvStripifier.findEdgeInfo(edgeInfos, faceInfo._v2, faceInfo._v0);
+
+ if (IsInStrip(currEdge._face0) || IsInStrip(currEdge._face1)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * take the given forward and backward strips and combine them together
+ *
+ * @param forward
+ * @param backward
+ */
+ void combine(final List<NvFaceInfo> forward, final List<NvFaceInfo> backward) {
+ // add backward faces
+ int numFaces = backward.size();
+ for (int i = numFaces - 1; i >= 0; i--) {
+ _faces.add(backward.get(i));
+ }
+
+ // add forward faces
+ numFaces = forward.size();
+ for (int i = 0; i < numFaces; i++) {
+ _faces.add(forward.get(i));
+ }
+ }
+
+ /**
+ * @param faceVec
+ * @param face
+ * @return true if the face is "unique", i.e. has a vertex which doesn't exist in the faceVec
+ */
+ boolean unique(final List<NvFaceInfo> faceVec, final NvFaceInfo face) {
+ boolean bv0, bv1, bv2; // bools to indicate whether a vertex is in the faceVec or not
+ bv0 = bv1 = bv2 = false;
+
+ for (int i = 0; i < faceVec.size(); i++) {
+ if (!bv0) {
+ if (faceVec.get(i)._v0 == face._v0 || faceVec.get(i)._v1 == face._v0 || faceVec.get(i)._v2 == face._v0) {
+ bv0 = true;
+ }
+ }
+
+ if (!bv1) {
+ if (faceVec.get(i)._v0 == face._v1 || faceVec.get(i)._v1 == face._v1 || faceVec.get(i)._v2 == face._v1) {
+ bv1 = true;
+ }
+ }
+
+ if (!bv2) {
+ if (faceVec.get(i)._v0 == face._v2 || faceVec.get(i)._v1 == face._v2 || faceVec.get(i)._v2 == face._v2) {
+ bv2 = true;
+ }
+ }
+
+ // the face is not unique, all its vertices exist in the face vector
+ if (bv0 && bv1 && bv2) {
+ return false;
+ }
+ }
+
+ // if we get out here, it's unique
+ return true;
+ }
+
+ /**
+ * If either the faceInfo has a real strip index because it is already assign to a committed strip OR it is assigned
+ * in an experiment and the experiment index is the one we are building for, then it is marked and unavailable
+ */
+ boolean isMarked(final NvFaceInfo faceInfo) {
+ return faceInfo._stripId >= 0 || isExperiment() && faceInfo._experimentId == _experimentId;
+ }
+
+ /**
+ * Marks the face with the current strip ID
+ *
+ * @param faceInfo
+ */
+ void markTriangle(final NvFaceInfo faceInfo) {
+ assert !isMarked(faceInfo);
+ if (isExperiment()) {
+ faceInfo._experimentId = _experimentId;
+ faceInfo._testStripId = _stripId;
+ } else {
+ faceInfo._experimentId = -1;
+ faceInfo._stripId = _stripId;
+ }
+ }
+
+ /**
+ * Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
+ *
+ * @param edgeInfos
+ * @param faceInfos
+ */
+ void build(final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> faceInfos) {
+ // used in building the strips forward and backward
+ final List<Integer> scratchIndices = Lists.newArrayList();
+
+ // build forward... start with the initial face
+ final List<NvFaceInfo> forwardFaces = Lists.newArrayList();
+ final List<NvFaceInfo> backwardFaces = Lists.newArrayList();
+ forwardFaces.add(_startInfo._startFace);
+
+ markTriangle(_startInfo._startFace);
+
+ final int v0 = _startInfo._toV1 ? _startInfo._startEdge._v0 : _startInfo._startEdge._v1;
+ final int v1 = _startInfo._toV1 ? _startInfo._startEdge._v1 : _startInfo._startEdge._v0;
+
+ // easiest way to get v2 is to use this function which requires the
+ // other indices to already be in the list.
+ scratchIndices.add(v0);
+ scratchIndices.add(v1);
+ final int v2 = NvStripifier.getNextIndex(scratchIndices, _startInfo._startFace);
+ scratchIndices.add(v2);
+
+ //
+ // build the forward list
+ //
+ int nv0 = v1;
+ int nv1 = v2;
+
+ NvFaceInfo nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, _startInfo._startFace);
+ while (nextFace != null && !isMarked(nextFace)) {
+ // check to see if this next face is going to cause us to die soon
+ int testnv0 = nv1;
+ final int testnv1 = NvStripifier.getNextIndex(scratchIndices, nextFace);
+
+ final NvFaceInfo nextNextFace = NvStripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);
+
+ if (nextNextFace == null || isMarked(nextNextFace)) {
+ // uh, oh, we're following a dead end, try swapping
+ final NvFaceInfo testNextFace = NvStripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
+
+ if (testNextFace != null && !isMarked(testNextFace)) {
+ // we only swap if it buys us something
+
+ // add a "fake" degenerate face
+ final NvFaceInfo tempFace = new NvFaceInfo(nv0, nv1, nv0, true);
+
+ forwardFaces.add(tempFace);
+ markTriangle(tempFace);
+
+ scratchIndices.add(nv0);
+ testnv0 = nv0;
+
+ ++_numDegenerates;
+ }
+
+ }
+
+ // add this to the strip
+ forwardFaces.add(nextFace);
+
+ markTriangle(nextFace);
+
+ // add the index
+ // nv0 = nv1;
+ // nv1 = NvStripifier.GetNextIndex(scratchIndices, nextFace);
+ scratchIndices.add(testnv1);
+
+ // and get the next face
+ nv0 = testnv0;
+ nv1 = testnv1;
+
+ nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
+
+ }
+
+ // tempAllFaces is going to be forwardFaces + backwardFaces
+ // it's used for Unique()
+ final List<NvFaceInfo> tempAllFaces = Lists.newArrayList();
+ for (int i = 0; i < forwardFaces.size(); i++) {
+ tempAllFaces.add(forwardFaces.get(i));
+ }
+
+ //
+ // reset the indices for building the strip backwards and do so
+ //
+ scratchIndices.clear();
+ scratchIndices.add(v2);
+ scratchIndices.add(v1);
+ scratchIndices.add(v0);
+ nv0 = v1;
+ nv1 = v0;
+ nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, _startInfo._startFace);
+ while (nextFace != null && !isMarked(nextFace)) {
+ // this tests to see if a face is "unique", meaning that its vertices aren't already in the list
+ // so, strips which "wrap-around" are not allowed
+ if (!unique(tempAllFaces, nextFace)) {
+ break;
+ }
+
+ // check to see if this next face is going to cause us to die soon
+ int testnv0 = nv1;
+ final int testnv1 = NvStripifier.getNextIndex(scratchIndices, nextFace);
+
+ final NvFaceInfo nextNextFace = NvStripifier.findOtherFace(edgeInfos, testnv0, testnv1, nextFace);
+
+ if (nextNextFace == null || isMarked(nextNextFace)) {
+ // uh, oh, we're following a dead end, try swapping
+ final NvFaceInfo testNextFace = NvStripifier.findOtherFace(edgeInfos, nv0, testnv1, nextFace);
+ if (testNextFace != null && !isMarked(testNextFace)) {
+ // we only swap if it buys us something
+
+ // add a "fake" degenerate face
+ final NvFaceInfo tempFace = new NvFaceInfo(nv0, nv1, nv0, true);
+
+ backwardFaces.add(tempFace);
+ markTriangle(tempFace);
+ scratchIndices.add(nv0);
+ testnv0 = nv0;
+
+ ++_numDegenerates;
+ }
+
+ }
+
+ // add this to the strip
+ backwardFaces.add(nextFace);
+
+ // this is just so Unique() will work
+ tempAllFaces.add(nextFace);
+
+ markTriangle(nextFace);
+
+ // add the index
+ // nv0 = nv1;
+ // nv1 = NvStripifier.GetNextIndex(scratchIndices, nextFace);
+ scratchIndices.add(testnv1);
+
+ // and get the next face
+ nv0 = testnv0;
+ nv1 = testnv1;
+ nextFace = NvStripifier.findOtherFace(edgeInfos, nv0, nv1, nextFace);
+ }
+
+ // Combine the forward and backwards stripification lists and put into our own face vector
+ combine(forwardFaces, backwardFaces);
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java
index 4878058..cecb0d3 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripStartInfo.java
@@ -1,26 +1,26 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-/**
- * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
- */
-final class NvStripStartInfo {
- NvFaceInfo _startFace;
- NvEdgeInfo _startEdge;
- boolean _toV1;
-
- NvStripStartInfo(final NvFaceInfo startFace, final NvEdgeInfo startEdge, final boolean toV1) {
- _startFace = startFace;
- _startEdge = startEdge;
- _toV1 = toV1;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+/**
+ * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
+ */
+final class NvStripStartInfo {
+ NvFaceInfo _startFace;
+ NvEdgeInfo _startEdge;
+ boolean _toV1;
+
+ NvStripStartInfo(final NvFaceInfo startFace, final NvEdgeInfo startEdge, final boolean toV1) {
+ _startFace = startFace;
+ _startEdge = startEdge;
+ _toV1 = toV1;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java
index 0bcca22..8779e5e 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvStripifier.java
@@ -1,1297 +1,1297 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-import java.util.List;
-import java.util.Set;
-import java.util.logging.Logger;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-/**
- * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
- */
-final class NvStripifier {
- private static final Logger logger = Logger.getLogger(NvStripifier.class.getName());
-
- public static int CACHE_INEFFICIENCY = 6;
-
- protected List<Integer> _indices = Lists.newArrayList();
- protected int _cacheSize;
- protected int _minStripLength;
- protected float _meshJump;
- protected boolean _firstTimeResetPoint;
-
- /**
- *
- * @param in_indices
- * the input indices of the mesh to stripify
- * @param in_cacheSize
- * the target cache size
- * @param in_minStripLength
- * @param maxIndex
- * @param outStrips
- * @param outFaceList
- */
- void stripify(final List<Integer> in_indices, final int in_cacheSize, final int in_minStripLength,
- final int maxIndex, final List<NvStripInfo> outStrips, final List<NvFaceInfo> outFaceList) {
- _meshJump = 0.0f;
- _firstTimeResetPoint = true; // used in FindGoodResetPoint()
-
- // the number of times to run the experiments
- final int numSamples = 10;
-
- // the cache size, clamped to one
- _cacheSize = Math.max(1, in_cacheSize - NvStripifier.CACHE_INEFFICIENCY);
-
- // this is the strip size threshold below which we dump the strip into a list
- _minStripLength = in_minStripLength;
-
- _indices = in_indices;
-
- // build the stripification info
- final List<NvFaceInfo> allFaceInfos = Lists.newArrayList();
- final List<NvEdgeInfo> allEdgeInfos = Lists.newArrayList();
-
- buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex);
-
- final List<NvStripInfo> allStrips = Lists.newArrayList();
-
- // stripify
- findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
-
- // split up the strips into cache friendly pieces, optimize them, then dump these into outStrips
- splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, outFaceList);
- }
-
- /**
- * Generates actual strips from the list-in-strip-order.
- *
- * @param allStrips
- * @param stripIndices
- * @param bStitchStrips
- * @param numSeparateStrips
- * @param bRestart
- * @param restartVal
- */
- int createStrips(final List<NvStripInfo> allStrips, final List<Integer> stripIndices, final boolean bStitchStrips,
- final boolean bRestart, final int restartVal) {
- int numSeparateStrips = 0;
-
- NvFaceInfo tLastFace = new NvFaceInfo(0, 0, 0);
- final int nStripCount = allStrips.size();
- assert nStripCount > 0;
-
- // we infer the cw/ccw ordering depending on the number of indices
- // this is screwed up by the fact that we insert -1s to denote changing strips
- // this is to account for that
- int accountForNegatives = 0;
-
- for (int i = 0; i < nStripCount; i++) {
- final NvStripInfo strip = allStrips.get(i);
- final int nStripFaceCount = strip._faces.size();
- assert nStripFaceCount > 0;
-
- // Handle the first face in the strip
- {
- final NvFaceInfo tFirstFace = new NvFaceInfo(strip._faces.get(0)._v0, strip._faces.get(0)._v1,
- strip._faces.get(0)._v2);
-
- // If there is a second face, reorder vertices such that the
- // unique vertex is first
- if (nStripFaceCount > 1) {
- final int nUnique = NvStripifier.getUniqueVertexInB(strip._faces.get(1), tFirstFace);
- if (nUnique == tFirstFace._v1) {
- final int store = tFirstFace._v1;
- tFirstFace._v1 = tFirstFace._v0;
- tFirstFace._v0 = store;
- } else if (nUnique == tFirstFace._v2) {
- final int store = tFirstFace._v2;
- tFirstFace._v2 = tFirstFace._v0;
- tFirstFace._v0 = store;
- }
-
- // If there is a third face, reorder vertices such that the
- // shared vertex is last
- if (nStripFaceCount > 2) {
- if (NvStripifier.isDegenerate(strip._faces.get(1))) {
- final int pivot = strip._faces.get(1)._v1;
- if (tFirstFace._v1 == pivot) {
- final int store = tFirstFace._v2;
- tFirstFace._v2 = tFirstFace._v1;
- tFirstFace._v1 = store;
- }
- } else {
- final int[] nShared = NvStripifier.getSharedVertices(strip._faces.get(2), tFirstFace);
- if (nShared[0] == tFirstFace._v1 && nShared[1] == -1) {
- final int store = tFirstFace._v2;
- tFirstFace._v2 = tFirstFace._v1;
- tFirstFace._v1 = store;
- }
- }
- }
- }
-
- if (i == 0 || !bStitchStrips || bRestart) {
- if (!NvStripifier.isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) {
- stripIndices.add(tFirstFace._v0);
- }
- } else {
- // Double tap the first in the new strip
- stripIndices.add(tFirstFace._v0);
-
- // Check CW/CCW ordering
- if (NvStripifier.nextIsCW(stripIndices.size() - accountForNegatives) != NvStripifier.isCW(
- strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) {
- stripIndices.add(tFirstFace._v0);
- }
- }
-
- stripIndices.add(tFirstFace._v0);
- stripIndices.add(tFirstFace._v1);
- stripIndices.add(tFirstFace._v2);
-
- // Update last face info
- tLastFace = tFirstFace;
- }
-
- for (int j = 1; j < nStripFaceCount; j++) {
- final int nUnique = NvStripifier.getUniqueVertexInB(tLastFace, strip._faces.get(j));
- if (nUnique != -1) {
- stripIndices.add(nUnique);
-
- // Update last face info
- tLastFace._v0 = tLastFace._v1;
- tLastFace._v1 = tLastFace._v2;
- tLastFace._v2 = nUnique;
- } else {
- // we've hit a degenerate
- stripIndices.add(strip._faces.get(j)._v2);
- tLastFace._v0 = strip._faces.get(j)._v0;// tLastFace.m_v1;
- tLastFace._v1 = strip._faces.get(j)._v1;// tLastFace.m_v2;
- tLastFace._v2 = strip._faces.get(j)._v2;// tLastFace.m_v1;
-
- }
- }
-
- // Double tap between strips.
- if (bStitchStrips && !bRestart) {
- if (i != nStripCount - 1) {
- stripIndices.add(tLastFace._v2);
- }
- } else if (bRestart) {
- stripIndices.add(restartVal);
- } else {
- // -1 index indicates next strip
- stripIndices.add(-1);
- accountForNegatives++;
- numSeparateStrips++;
- }
-
- // Update last face info
- tLastFace._v0 = tLastFace._v1;
- tLastFace._v1 = tLastFace._v2;
- // tLastFace._v2 = tLastFace._v2; // for info purposes.
- }
-
- if (bStitchStrips || bRestart) {
- numSeparateStrips = 1;
- }
-
- return numSeparateStrips;
- }
-
- /**
- * @param faceA
- * @param faceB
- * @return the first vertex unique to faceB
- */
- static int getUniqueVertexInB(final NvFaceInfo faceA, final NvFaceInfo faceB) {
- final int facev0 = faceB._v0;
- if (facev0 != faceA._v0 && facev0 != faceA._v1 && facev0 != faceA._v2) {
- return facev0;
- }
-
- final int facev1 = faceB._v1;
- if (facev1 != faceA._v0 && facev1 != faceA._v1 && facev1 != faceA._v2) {
- return facev1;
- }
-
- final int facev2 = faceB._v2;
- if (facev2 != faceA._v0 && facev2 != faceA._v1 && facev2 != faceA._v2) {
- return facev2;
- }
-
- // nothing is different
- return -1;
- }
-
- /**
- * @param faceA
- * @param faceB
- * @return the (at most) two vertices shared between the two faces
- */
- static int[] getSharedVertices(final NvFaceInfo faceA, final NvFaceInfo faceB) {
- final int[] vertexStore = new int[2];
- vertexStore[0] = vertexStore[1] = -1;
-
- final int facev0 = faceB._v0;
- if (facev0 == faceA._v0 || facev0 == faceA._v1 || facev0 == faceA._v2) {
- if (vertexStore[0] == -1) {
- vertexStore[0] = facev0;
- } else {
- vertexStore[1] = facev0;
- return vertexStore;
- }
- }
-
- final int facev1 = faceB._v1;
- if (facev1 == faceA._v0 || facev1 == faceA._v1 || facev1 == faceA._v2) {
- if (vertexStore[0] == -1) {
- vertexStore[0] = facev1;
- } else {
- vertexStore[1] = facev1;
- return vertexStore;
- }
- }
-
- final int facev2 = faceB._v2;
- if (facev2 == faceA._v0 || facev2 == faceA._v1 || facev2 == faceA._v2) {
- if (vertexStore[0] == -1) {
- vertexStore[0] = facev2;
- } else {
- vertexStore[1] = facev2;
- return vertexStore;
- }
- }
-
- return vertexStore;
- }
-
- static boolean isDegenerate(final NvFaceInfo face) {
- if (face._v0 == face._v1) {
- return true;
- } else if (face._v0 == face._v2) {
- return true;
- } else if (face._v1 == face._v2) {
- return true;
- } else {
- return false;
- }
- }
-
- static boolean isDegenerate(final int v0, final int v1, final int v2) {
- if (v0 == v1) {
- return true;
- } else if (v0 == v2) {
- return true;
- } else if (v1 == v2) {
- return true;
- } else {
- return false;
- }
- }
-
- // ///////////////////////////////////////////////////////////////////////////////
- //
- // Big mess of functions called during stripification
- // Note: I removed some that were orphans - JES
- //
- // ///////////////////////////////////////////////////////////////////////////////
-
- /**
- * @param faceInfo
- * @param v0
- * @param v1
- * @return true if the face is ordered in CW fashion
- */
- static boolean isCW(final NvFaceInfo faceInfo, final int v0, final int v1) {
- if (faceInfo._v0 == v0) {
- return faceInfo._v1 == v1;
- } else if (faceInfo._v1 == v0) {
- return faceInfo._v2 == v1;
- } else {
- return faceInfo._v0 == v1;
- }
- }
-
- /**
- *
- * @param numIndices
- * @return true if the next face should be ordered in CW fashion
- */
- static boolean nextIsCW(final int numIndices) {
- return numIndices % 2 == 0;
- }
-
- /**
- * @param indices
- * @param face
- * @return vertex of the input face which is "next" in the input index list
- */
- static int getNextIndex(final List<Integer> indices, final NvFaceInfo face) {
- final int numIndices = indices.size();
- assert numIndices >= 2;
-
- final int v0 = indices.get(numIndices - 2);
- final int v1 = indices.get(numIndices - 1);
-
- final int fv0 = face._v0;
- final int fv1 = face._v1;
- final int fv2 = face._v2;
-
- if (fv0 != v0 && fv0 != v1) {
- if (fv1 != v0 && fv1 != v1 || fv2 != v0 && fv2 != v1) {
- NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n");
- NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n");
- }
- return fv0;
- }
- if (fv1 != v0 && fv1 != v1) {
- if (fv0 != v0 && fv0 != v1 || fv2 != v0 && fv2 != v1) {
- NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n");
- NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n");
- }
- return fv1;
- }
- if (fv2 != v0 && fv2 != v1) {
- if (fv0 != v0 && fv0 != v1 || fv1 != v0 && fv1 != v1) {
- NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n");
- NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n");
- }
- return fv2;
- }
-
- // shouldn't get here, but let's try and fail gracefully
- if (fv0 == fv1 || fv0 == fv2) {
- return fv0;
- } else if (fv1 == fv0 || fv1 == fv2) {
- return fv1;
- } else if (fv2 == fv0 || fv2 == fv1) {
- return fv2;
- } else {
- return -1;
- }
- }
-
- /**
- * find the edge info for these two indices
- *
- * @param edgeInfos
- * @param v0
- * @param v1
- * @return
- */
- static NvEdgeInfo findEdgeInfo(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1) {
- // we can get to it through either array because the edge infos have a v0 and v1 and there is no order except
- // how it was first created.
- NvEdgeInfo infoIter = edgeInfos.get(v0);
- while (infoIter != null) {
- if (infoIter._v0 == v0) {
- if (infoIter._v1 == v1) {
- return infoIter;
- } else {
- infoIter = infoIter._nextV0;
- }
- } else {
- assert infoIter._v1 == v0;
- if (infoIter._v0 == v1) {
- return infoIter;
- } else {
- infoIter = infoIter._nextV1;
- }
- }
- }
- return null;
- }
-
- /**
- * find the other face sharing these vertices
- *
- * @param edgeInfos
- * @param v0
- * @param v1
- * @param faceInfo
- * @return
- */
- static NvFaceInfo findOtherFace(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1,
- final NvFaceInfo faceInfo) {
- final NvEdgeInfo edgeInfo = NvStripifier.findEdgeInfo(edgeInfos, v0, v1);
-
- if (edgeInfo == null && v0 == v1) {
- // we've hit a degenerate
- return null;
- }
-
- assert edgeInfo != null;
- return edgeInfo._face0 == faceInfo ? edgeInfo._face1 : edgeInfo._face0;
- }
-
- /**
- * A good reset point is one near other committed areas so that we know that when we've made the longest strips its
- * because we're stripifying in the same general orientation.
- *
- * @param faceInfos
- * @param edgeInfos
- * @return
- */
- NvFaceInfo findGoodResetPoint(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos) {
- // we hop into different areas of the mesh to try to get other large open spans done. Areas of small strips can
- // just be left to triangle lists added at the end.
- NvFaceInfo result = null;
- final int numFaces = faceInfos.size();
- int startPoint;
- if (_firstTimeResetPoint) {
- // first time, find a face with few neighbors (look for an edge of the mesh)
- startPoint = findStartPoint(faceInfos, edgeInfos);
- _firstTimeResetPoint = false;
- } else {
- startPoint = (int) (((float) numFaces - 1) * _meshJump);
- }
-
- if (startPoint == -1) {
- startPoint = (int) (((float) numFaces - 1) * _meshJump);
-
- // meshJump += 0.1f;
- // if (meshJump > 1.0f)
- // meshJump = .05f;
- }
-
- int i = startPoint;
- do {
-
- // if this guy isn't visited, try him
- if (faceInfos.get(i)._stripId < 0) {
- result = faceInfos.get(i);
- break;
- }
-
- // update the index and clamp to 0-(numFaces-1)
- if (++i >= numFaces) {
- i = 0;
- }
-
- } while (i != startPoint);
-
- // update the meshJump
- _meshJump += 0.1f;
- if (_meshJump > 1.0f) {
- _meshJump = .05f;
- }
-
- // return the best face we found
- return result;
- }
-
- /**
- * Does the stripification, puts output strips into vector allStrips
- *
- * Works by setting running a number of experiments in different areas of the mesh, and accepting the one which
- * results in the longest strips. It then accepts this, and moves on to a different area of the mesh. We try to jump
- * around the mesh some, to ensure that large open spans of strips get generated.
- *
- * @param allStrips
- * @param allFaceInfos
- * @param allEdgeInfos
- * @param numSamples
- */
- @SuppressWarnings("unchecked")
- void findAllStrips(final List<NvStripInfo> allStrips, final List<NvFaceInfo> allFaceInfos,
- final List<NvEdgeInfo> allEdgeInfos, final int numSamples) {
- // the experiments
- int experimentId = 0;
- int stripId = 0;
- boolean done = false;
-
- while (!done) {
-
- //
- // PHASE 1: Set up numSamples * numEdges experiments
- //
- final List<NvStripInfo>[] experiments = new List[numSamples * 6];
- for (int i = 0; i < experiments.length; i++) {
- experiments[i] = Lists.newArrayList();
- }
-
- int experimentIndex = 0;
- final Set<NvFaceInfo> resetPoints = Sets.newHashSet();
- for (int i = 0; i < numSamples; i++) {
-
- // Try to find another good reset point.
- // If there are none to be found, we are done
- final NvFaceInfo nextFace = findGoodResetPoint(allFaceInfos, allEdgeInfos);
- if (nextFace == null) {
- done = true;
- break;
- }
- // If we have already evaluated starting at this face in this slew
- // of experiments, then skip going any further
- else if (resetPoints.contains(nextFace)) {
- continue;
- }
-
- // trying it now...
- resetPoints.add(nextFace);
-
- // otherwise, we shall now try experiments for starting on the 01,12, and 20 edges
- assert nextFace._stripId < 0;
-
- // build the strip off of this face's 0-1 edge
- final NvEdgeInfo edge01 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v0, nextFace._v1);
- final NvStripInfo strip01 = new NvStripInfo(new NvStripStartInfo(nextFace, edge01, true), stripId++,
- experimentId++);
- experiments[experimentIndex++].add(strip01);
-
- // build the strip off of this face's 1-0 edge
- final NvEdgeInfo edge10 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v0, nextFace._v1);
- final NvStripInfo strip10 = new NvStripInfo(new NvStripStartInfo(nextFace, edge10, false), stripId++,
- experimentId++);
- experiments[experimentIndex++].add(strip10);
-
- // build the strip off of this face's 1-2 edge
- final NvEdgeInfo edge12 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v1, nextFace._v2);
- final NvStripInfo strip12 = new NvStripInfo(new NvStripStartInfo(nextFace, edge12, true), stripId++,
- experimentId++);
- experiments[experimentIndex++].add(strip12);
-
- // build the strip off of this face's 2-1 edge
- final NvEdgeInfo edge21 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v1, nextFace._v2);
- final NvStripInfo strip21 = new NvStripInfo(new NvStripStartInfo(nextFace, edge21, false), stripId++,
- experimentId++);
- experiments[experimentIndex++].add(strip21);
-
- // build the strip off of this face's 2-0 edge
- final NvEdgeInfo edge20 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v2, nextFace._v0);
- final NvStripInfo strip20 = new NvStripInfo(new NvStripStartInfo(nextFace, edge20, true), stripId++,
- experimentId++);
- experiments[experimentIndex++].add(strip20);
-
- // build the strip off of this face's 0-2 edge
- final NvEdgeInfo edge02 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v2, nextFace._v0);
- final NvStripInfo strip02 = new NvStripInfo(new NvStripStartInfo(nextFace, edge02, false), stripId++,
- experimentId++);
- experiments[experimentIndex++].add(strip02);
- }
-
- //
- // PHASE 2: Iterate through that we setup in the last phase
- // and really build each of the strips and strips that follow to see how
- // far we get
- //
- final int numExperiments = experimentIndex;
- for (int i = 0; i < numExperiments; i++) {
- // get the strip set
- NvStripInfo stripIter = experiments[i].get(0);
-
- // build the first strip of the list
- stripIter.build(allEdgeInfos, allFaceInfos);
- final int currExperimentId = stripIter._experimentId;
-
- final NvStripStartInfo startInfo = new NvStripStartInfo(null, null, false);
- while (findTraversal(allFaceInfos, allEdgeInfos, stripIter, startInfo)) {
-
- // create the new strip info
- stripIter = new NvStripInfo(startInfo, stripId++, currExperimentId);
-
- // build the next strip
- stripIter.build(allEdgeInfos, allFaceInfos);
-
- // add it to the list
- experiments[i].add(stripIter);
- }
- }
-
- //
- // Phase 3: Find the experiment that has the most promise
- //
- int bestIndex = 0;
- double bestValue = 0;
- for (int i = 0; i < numExperiments; i++) {
- final float avgStripSizeWeight = 1.0f;
- // final float numTrisWeight = 0.0f; // unused
- final float numStripsWeight = 0.0f;
- final float avgStripSize = avgStripSize(experiments[i]);
- final float numStrips = experiments[i].size();
- final float value = avgStripSize * avgStripSizeWeight + numStrips * numStripsWeight;
- // float value = 1.f / numStrips;
- // float value = numStrips * avgStripSize;
-
- if (value > bestValue) {
- bestValue = value;
- bestIndex = i;
- }
- }
-
- //
- // Phase 4: commit the best experiment of the bunch
- //
- commitStrips(allStrips, experiments[bestIndex]);
- }
- }
-
- /**
- * Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then reorders these pieces
- * to maximize cache hits. The final strips are stored in outStrips
- *
- * @param allStrips
- * @param outStrips
- * @param edgeInfos
- * @param outFaceList
- */
- void splitUpStripsAndOptimize(final List<NvStripInfo> allStrips, final List<NvStripInfo> outStrips,
- final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> outFaceList) {
- final int threshold = _cacheSize;
- final List<NvStripInfo> tempStrips = Lists.newArrayList();
-
- // split up strips into threshold-sized pieces
- for (int i = 0; i < allStrips.size(); i++) {
- final NvStripInfo allStripI = allStrips.get(i);
- NvStripInfo currentStrip;
- final NvStripStartInfo startInfo = new NvStripStartInfo(null, null, false);
-
- int actualStripSize = 0;
- for (final NvFaceInfo face : allStripI._faces) {
- if (!NvStripifier.isDegenerate(face)) {
- actualStripSize++;
- }
- }
-
- if (actualStripSize > threshold) {
-
- final int numTimes = actualStripSize / threshold;
- int numLeftover = actualStripSize % threshold;
-
- int degenerateCount = 0, j = 0;
- for (; j < numTimes; j++) {
- currentStrip = new NvStripInfo(startInfo, 0, -1);
-
- int faceCtr = j * threshold + degenerateCount;
- boolean bFirstTime = true;
- while (faceCtr < threshold + j * threshold + degenerateCount) {
- if (NvStripifier.isDegenerate(allStripI._faces.get(faceCtr))) {
- degenerateCount++;
-
- // last time or first time through, no need for a degenerate
- if ((faceCtr + 1 != threshold + j * threshold + degenerateCount || j == numTimes - 1
- && numLeftover < 4 && numLeftover > 0)
- && !bFirstTime) {
- currentStrip._faces.add(allStripI._faces.get(faceCtr++));
- } else {
- ++faceCtr;
- }
- } else {
- currentStrip._faces.add(allStripI._faces.get(faceCtr++));
- bFirstTime = false;
- }
- }
- if (j == numTimes - 1) // last time through
- {
- if (numLeftover < 4 && numLeftover > 0) // way too small
- {
- // just add to last strip
- int ctr = 0;
- while (ctr < numLeftover) {
- if (NvStripifier.isDegenerate(allStripI._faces.get(faceCtr))) {
- ++degenerateCount;
- } else {
- ++ctr;
- }
- currentStrip._faces.add(allStripI._faces.get(faceCtr++));
- }
- numLeftover = 0;
- }
- }
- tempStrips.add(currentStrip);
- }
- int leftOff = j * threshold + degenerateCount;
-
- if (numLeftover != 0) {
- currentStrip = new NvStripInfo(startInfo, 0, -1);
-
- int ctr = 0;
- boolean bFirstTime = true;
- while (ctr < numLeftover) {
- if (!NvStripifier.isDegenerate(allStripI._faces.get(leftOff))) {
- ctr++;
- bFirstTime = false;
- currentStrip._faces.add(allStripI._faces.get(leftOff++));
- } else if (!bFirstTime) {
- currentStrip._faces.add(allStripI._faces.get(leftOff++));
- } else {
- leftOff++;
- }
- }
-
- tempStrips.add(currentStrip);
- }
- } else {
- // we're not just doing a tempStrips.add(allBigStrips.get(i)) because
- // this way we can delete allBigStrips later to free the memory
- currentStrip = new NvStripInfo(startInfo, 0, -1);
-
- for (int j = 0; j < allStripI._faces.size(); j++) {
- currentStrip._faces.add(allStripI._faces.get(j));
- }
-
- tempStrips.add(currentStrip);
- }
- }
-
- // add small strips to face list
- final List<NvStripInfo> tempStrips2 = Lists.newArrayList();
- removeSmallStrips(tempStrips, tempStrips2, outFaceList);
-
- outStrips.clear();
- // screw optimization for now
- // for(i = 0; i < tempStrips.size(); ++i)
- // outStrips.add(tempStrips.get(i));
-
- if (tempStrips2.size() != 0) {
- // Optimize for the vertex cache
- final VertexCache vcache = new VertexCache(_cacheSize);
-
- float bestNumHits = -1.0f;
- float numHits;
- int bestIndex = 0;
- int firstIndex = 0;
- float minCost = 10000.0f;
-
- for (int i = 0; i < tempStrips2.size(); i++) {
- final NvStripInfo tempStrips2I = tempStrips2.get(i);
- int numNeighbors = 0;
-
- // find strip with least number of neighbors per face
- for (int j = 0; j < tempStrips2I._faces.size(); j++) {
- numNeighbors += numNeighbors(tempStrips2I._faces.get(j), edgeInfos);
- }
-
- final float currCost = numNeighbors / (float) tempStrips2I._faces.size();
- if (currCost < minCost) {
- minCost = currCost;
- firstIndex = i;
- }
- }
-
- final NvStripInfo tempStrips2FirstIndex = tempStrips2.get(firstIndex);
- updateCacheStrip(vcache, tempStrips2FirstIndex);
- outStrips.add(tempStrips2FirstIndex);
-
- tempStrips2FirstIndex._visited = true;
-
- boolean bWantsCW = tempStrips2FirstIndex._faces.size() % 2 == 0;
-
- // XXX: this n^2 algo is what slows down stripification so much.... needs to be improved
- while (true) {
- bestNumHits = -1.0f;
-
- // find best strip to add next, given the current cache
- for (int i = 0; i < tempStrips2.size(); i++) {
- final NvStripInfo tempStrips2I = tempStrips2.get(i);
- if (tempStrips2I._visited) {
- continue;
- }
-
- numHits = calcNumHitsStrip(vcache, tempStrips2I);
- if (numHits > bestNumHits) {
- bestNumHits = numHits;
- bestIndex = i;
- } else if (numHits >= bestNumHits) {
- // check previous strip to see if this one requires it to switch polarity
- final NvStripInfo strip = tempStrips2I;
- final int nStripFaceCount = strip._faces.size();
-
- final NvFaceInfo tFirstFace = new NvFaceInfo(strip._faces.get(0));
-
- // If there is a second face, reorder vertices such that the
- // unique vertex is first
- if (nStripFaceCount > 1) {
- final int nUnique = NvStripifier.getUniqueVertexInB(strip._faces.get(1), tFirstFace);
- if (nUnique == tFirstFace._v1) {
- final int store = tFirstFace._v1;
- tFirstFace._v1 = tFirstFace._v0;
- tFirstFace._v0 = store;
- } else if (nUnique == tFirstFace._v2) {
- final int store = tFirstFace._v2;
- tFirstFace._v2 = tFirstFace._v0;
- tFirstFace._v0 = store;
- }
-
- // If there is a third face, reorder vertices such that the
- // shared vertex is last
- if (nStripFaceCount > 2) {
- final int[] nShared = NvStripifier.getSharedVertices(strip._faces.get(2), tFirstFace);
- if (nShared[0] == tFirstFace._v1 && nShared[1] == -1) {
- final int store = tFirstFace._v2;
- tFirstFace._v2 = tFirstFace._v1;
- tFirstFace._v1 = store;
- }
- }
- }
-
- // Check CW/CCW ordering
- if (bWantsCW == NvStripifier.isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) {
- // I like this one!
- bestIndex = i;
- }
- }
- }
-
- if (bestNumHits == -1.0f) {
- break;
- }
- tempStrips2.get(bestIndex)._visited = true;
- updateCacheStrip(vcache, tempStrips2.get(bestIndex));
- outStrips.add(tempStrips2.get(bestIndex));
- bWantsCW = tempStrips2.get(bestIndex)._faces.size() % 2 == 0 ? bWantsCW : !bWantsCW;
- }
- }
- }
-
- /**
- * @param allStrips
- * the whole strip vector...all small strips will be deleted from this list, to avoid leaking mem
- * @param allBigStrips
- * an out parameter which will contain all strips above minStripLength
- * @param faceList
- * an out parameter which will contain all faces which were removed from the striplist
- */
- void removeSmallStrips(final List<NvStripInfo> allStrips, final List<NvStripInfo> allBigStrips,
- final List<NvFaceInfo> faceList) {
- faceList.clear();
- allBigStrips.clear(); // make sure these are empty
- final List<NvFaceInfo> tempFaceList = Lists.newArrayList();
-
- for (int i = 0; i < allStrips.size(); i++) {
- final NvStripInfo allStripI = allStrips.get(i);
- if (allStripI._faces.size() < _minStripLength) {
- // strip is too small, add faces to faceList
- for (int j = 0; j < allStripI._faces.size(); j++) {
- tempFaceList.add(allStripI._faces.get(j));
- }
- } else {
- allBigStrips.add(allStripI);
- }
- }
-
- if (!tempFaceList.isEmpty()) {
- final boolean[] bVisitedList = new boolean[tempFaceList.size()];
- final VertexCache vcache = new VertexCache(_cacheSize);
-
- int bestNumHits = -1;
- int numHits = 0;
- int bestIndex = 0;
-
- while (true) {
- bestNumHits = -1;
-
- // find best face to add next, given the current cache
- for (int i = 0; i < tempFaceList.size(); i++) {
- if (bVisitedList[i]) {
- continue;
- }
-
- numHits = calcNumHitsFace(vcache, tempFaceList.get(i));
- if (numHits > bestNumHits) {
- bestNumHits = numHits;
- bestIndex = i;
- }
- }
-
- if (bestNumHits == -1.0f) {
- break;
- }
- bVisitedList[bestIndex] = true;
- updateCacheFace(vcache, tempFaceList.get(bestIndex));
- faceList.add(tempFaceList.get(bestIndex));
- }
- }
- }
-
- /**
- * Finds the next face to start the next strip on.
- *
- * @param faceInfos
- * @param edgeInfos
- * @param strip
- * @param startInfo
- * @return
- */
- boolean findTraversal(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos, final NvStripInfo strip,
- final NvStripStartInfo startInfo) {
- // if the strip was v0.v1 on the edge, then v1 will be a vertex in the next edge.
- final int v = strip._startInfo._toV1 ? strip._startInfo._startEdge._v1 : strip._startInfo._startEdge._v0;
-
- NvFaceInfo untouchedFace = null;
- NvEdgeInfo edgeIter = edgeInfos.get(v);
- while (edgeIter != null) {
- final NvFaceInfo face0 = edgeIter._face0;
- final NvFaceInfo face1 = edgeIter._face1;
- if (face0 != null && !strip.IsInStrip(face0) && face1 != null && !strip.isMarked(face1)) {
- untouchedFace = face1;
- break;
- }
- if (face1 != null && !strip.IsInStrip(face1) && face0 != null && !strip.isMarked(face0)) {
- untouchedFace = face0;
- break;
- }
-
- // find the next edgeIter
- edgeIter = edgeIter._v0 == v ? edgeIter._nextV0 : edgeIter._nextV1;
- }
-
- startInfo._startFace = untouchedFace;
- startInfo._startEdge = edgeIter;
- if (edgeIter != null) {
- if (strip.sharesEdge(startInfo._startFace, edgeInfos)) {
- startInfo._toV1 = edgeIter._v0 == v; // note! used to be m_v1
- } else {
- startInfo._toV1 = edgeIter._v1 == v;
- }
- }
- return startInfo._startFace != null;
- }
-
- /**
- * "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips vector
- *
- * @param allStrips
- * @param strips
- */
- void commitStrips(final List<NvStripInfo> allStrips, final List<NvStripInfo> strips) {
- // Iterate through strips
- for (final NvStripInfo strip : strips) {
- // Tell the strip that it is now real
- strip._experimentId = -1;
-
- // add to the list of real strips
- allStrips.add(strip);
-
- // Iterate through the faces of the strip
- // Tell the faces of the strip that they belong to a real strip now
- final List<NvFaceInfo> faces = strip._faces;
- for (final NvFaceInfo face : faces) {
- strip.markTriangle(face);
- }
- }
- }
-
- /**
- *
- * @param strips
- * @return the average strip size of the input vector of strips
- */
- float avgStripSize(final List<NvStripInfo> strips) {
- int sizeAccum = 0;
- for (final NvStripInfo strip : strips) {
- sizeAccum += strip._faces.size();
- sizeAccum -= strip._numDegenerates;
- }
- return (float) sizeAccum / (float) strips.size();
- }
-
- /**
- * Finds a good starting point, namely one which has only one neighbor
- *
- * @param faceInfos
- * @param edgeInfos
- * @return
- */
- int findStartPoint(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos) {
- int bestCtr = -1;
- int bestIndex = -1;
-
- int i = 0;
- for (final NvFaceInfo faceInfo : faceInfos) {
- int ctr = 0;
-
- if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v0, faceInfo._v1, faceInfo) == null) {
- ctr++;
- }
- if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v1, faceInfo._v2, faceInfo) == null) {
- ctr++;
- }
- if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v2, faceInfo._v0, faceInfo) == null) {
- ctr++;
- }
- if (ctr > bestCtr) {
- bestCtr = ctr;
- bestIndex = i;
- }
- i++;
- }
-
- if (bestCtr == 0) {
- return -1;
- } else {
- return bestIndex;
- }
- }
-
- /**
- * Updates the input vertex cache with this strip's vertices
- *
- * @param vcache
- * @param strip
- */
- void updateCacheStrip(final VertexCache vcache, final NvStripInfo strip) {
- for (final NvFaceInfo face : strip._faces) {
- updateCacheFace(vcache, face);
- }
- }
-
- /**
- * Updates the input vertex cache with this face's vertices
- *
- * @param vcache
- * @param face
- */
- void updateCacheFace(final VertexCache vcache, final NvFaceInfo face) {
- if (!vcache.inCache(face._v0)) {
- vcache.addEntry(face._v0);
- }
-
- if (!vcache.inCache(face._v1)) {
- vcache.addEntry(face._v1);
- }
-
- if (!vcache.inCache(face._v2)) {
- vcache.addEntry(face._v2);
- }
- }
-
- /**
- * @param vcache
- * @param strip
- * @return the number of cache hits per face in the strip
- */
- float calcNumHitsStrip(final VertexCache vcache, final NvStripInfo strip) {
- int numHits = 0;
- int numFaces = 0;
-
- for (final NvFaceInfo face : strip._faces) {
- if (vcache.inCache(face._v0)) {
- ++numHits;
- }
-
- if (vcache.inCache(face._v1)) {
- ++numHits;
- }
-
- if (vcache.inCache(face._v2)) {
- ++numHits;
- }
-
- numFaces++;
- }
-
- return (float) numHits / (float) numFaces;
- }
-
- /**
- * @param vcache
- * @param face
- * @return the number of cache hits in the face
- */
- int calcNumHitsFace(final VertexCache vcache, final NvFaceInfo face) {
- int numHits = 0;
-
- if (vcache.inCache(face._v0)) {
- numHits++;
- }
-
- if (vcache.inCache(face._v1)) {
- numHits++;
- }
-
- if (vcache.inCache(face._v2)) {
- numHits++;
- }
-
- return numHits;
- }
-
- /**
- *
- * @param face
- * @param edgeInfoVec
- * @return the number of neighbors that this face has
- */
- int numNeighbors(final NvFaceInfo face, final List<NvEdgeInfo> edgeInfoVec) {
- int numNeighbors = 0;
-
- if (NvStripifier.findOtherFace(edgeInfoVec, face._v0, face._v1, face) != null) {
- numNeighbors++;
- }
-
- if (NvStripifier.findOtherFace(edgeInfoVec, face._v1, face._v2, face) != null) {
- numNeighbors++;
- }
-
- if (NvStripifier.findOtherFace(edgeInfoVec, face._v2, face._v0, face) != null) {
- numNeighbors++;
- }
-
- return numNeighbors;
- }
-
- /**
- * Builds the list of all face and edge infos
- *
- * @param faceInfos
- * @param edgeInfos
- * @param maxIndex
- */
- void buildStripifyInfo(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos, final int maxIndex) {
- // reserve space for the face infos, but do not resize them.
- final int numIndices = _indices.size();
-
- // we actually resize the edge infos, so we must initialize to null
- for (int i = 0; i <= maxIndex; i++) {
- edgeInfos.add(null);
- }
-
- // iterate through the triangles of the triangle list
- final int numTriangles = numIndices / 3;
- int index = 0;
- final boolean[] bFaceUpdated = new boolean[3];
-
- for (int i = 0; i < numTriangles; i++) {
- boolean bMightAlreadyExist = true;
- bFaceUpdated[0] = false;
- bFaceUpdated[1] = false;
- bFaceUpdated[2] = false;
-
- // grab the indices
- final int v0 = _indices.get(index++);
- final int v1 = _indices.get(index++);
- final int v2 = _indices.get(index++);
-
- // we disregard degenerates
- if (NvStripifier.isDegenerate(v0, v1, v2)) {
- continue;
- }
-
- // create the face info and add it to the list of faces, but only if this exact face doesn't already
- // exist in the list
- final NvFaceInfo faceInfo = new NvFaceInfo(v0, v1, v2);
-
- // grab the edge infos, creating them if they do not already exist
- NvEdgeInfo edgeInfo01 = NvStripifier.findEdgeInfo(edgeInfos, v0, v1);
- if (edgeInfo01 == null) {
- // since one of it's edges isn't in the edge data structure, it can't already exist in the face
- // structure
- bMightAlreadyExist = false;
-
- // create the info
- edgeInfo01 = new NvEdgeInfo(v0, v1);
-
- // update the linked list on both
- edgeInfo01._nextV0 = edgeInfos.get(v0);
- edgeInfo01._nextV1 = edgeInfos.get(v1);
- edgeInfos.set(v0, edgeInfo01);
- edgeInfos.set(v1, edgeInfo01);
-
- // set face 0
- edgeInfo01._face0 = faceInfo;
- } else {
- if (edgeInfo01._face1 != null) {
- NvStripifier.logger
- .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
- } else {
- edgeInfo01._face1 = faceInfo;
- bFaceUpdated[0] = true;
- }
- }
-
- // grab the edge infos, creating them if they do not already exist
- NvEdgeInfo edgeInfo12 = NvStripifier.findEdgeInfo(edgeInfos, v1, v2);
- if (edgeInfo12 == null) {
- bMightAlreadyExist = false;
-
- // create the info
- edgeInfo12 = new NvEdgeInfo(v1, v2);
-
- // update the linked list on both
- edgeInfo12._nextV0 = edgeInfos.get(v1);
- edgeInfo12._nextV1 = edgeInfos.get(v2);
- edgeInfos.set(v1, edgeInfo12);
- edgeInfos.set(v2, edgeInfo12);
-
- // set face 0
- edgeInfo12._face0 = faceInfo;
- } else {
- if (edgeInfo12._face1 != null) {
- NvStripifier.logger
- .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
- } else {
- edgeInfo12._face1 = faceInfo;
- bFaceUpdated[1] = true;
- }
- }
-
- // grab the edge infos, creating them if they do not already exist
- NvEdgeInfo edgeInfo20 = NvStripifier.findEdgeInfo(edgeInfos, v2, v0);
- if (edgeInfo20 == null) {
- bMightAlreadyExist = false;
-
- // create the info
- edgeInfo20 = new NvEdgeInfo(v2, v0);
-
- // update the linked list on both
- edgeInfo20._nextV0 = edgeInfos.get(v2);
- edgeInfo20._nextV1 = edgeInfos.get(v0);
- edgeInfos.set(v2, edgeInfo20);
- edgeInfos.set(v0, edgeInfo20);
-
- // set face 0
- edgeInfo20._face0 = faceInfo;
- } else {
- if (edgeInfo20._face1 != null) {
- NvStripifier.logger
- .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
- } else {
- edgeInfo20._face1 = faceInfo;
- bFaceUpdated[2] = true;
- }
- }
-
- if (bMightAlreadyExist) {
- if (!alreadyExists(faceInfo, faceInfos)) {
- faceInfos.add(faceInfo);
- } else {
- // cleanup pointers that point to this deleted face
- if (bFaceUpdated[0]) {
- edgeInfo01._face1 = null;
- }
- if (bFaceUpdated[1]) {
- edgeInfo12._face1 = null;
- }
- if (bFaceUpdated[2]) {
- edgeInfo20._face1 = null;
- }
- }
- } else {
- faceInfos.add(faceInfo);
- }
- }
- }
-
- boolean alreadyExists(final NvFaceInfo toFind, final List<NvFaceInfo> faceInfos) {
- for (final NvFaceInfo face : faceInfos) {
- if (face._v0 == toFind._v0 && face._v1 == toFind._v1 && face._v2 == toFind._v2) {
- return true;
- }
- }
-
- return false;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
+ */
+final class NvStripifier {
+ private static final Logger logger = Logger.getLogger(NvStripifier.class.getName());
+
+ public static int CACHE_INEFFICIENCY = 6;
+
+ protected List<Integer> _indices = Lists.newArrayList();
+ protected int _cacheSize;
+ protected int _minStripLength;
+ protected float _meshJump;
+ protected boolean _firstTimeResetPoint;
+
+ /**
+ *
+ * @param in_indices
+ * the input indices of the mesh to stripify
+ * @param in_cacheSize
+ * the target cache size
+ * @param in_minStripLength
+ * @param maxIndex
+ * @param outStrips
+ * @param outFaceList
+ */
+ void stripify(final List<Integer> in_indices, final int in_cacheSize, final int in_minStripLength,
+ final int maxIndex, final List<NvStripInfo> outStrips, final List<NvFaceInfo> outFaceList) {
+ _meshJump = 0.0f;
+ _firstTimeResetPoint = true; // used in FindGoodResetPoint()
+
+ // the number of times to run the experiments
+ final int numSamples = 10;
+
+ // the cache size, clamped to one
+ _cacheSize = Math.max(1, in_cacheSize - NvStripifier.CACHE_INEFFICIENCY);
+
+ // this is the strip size threshold below which we dump the strip into a list
+ _minStripLength = in_minStripLength;
+
+ _indices = in_indices;
+
+ // build the stripification info
+ final List<NvFaceInfo> allFaceInfos = Lists.newArrayList();
+ final List<NvEdgeInfo> allEdgeInfos = Lists.newArrayList();
+
+ buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex);
+
+ final List<NvStripInfo> allStrips = Lists.newArrayList();
+
+ // stripify
+ findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
+
+ // split up the strips into cache friendly pieces, optimize them, then dump these into outStrips
+ splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, outFaceList);
+ }
+
+ /**
+ * Generates actual strips from the list-in-strip-order.
+ *
+ * @param allStrips
+ * @param stripIndices
+ * @param bStitchStrips
+ * @param numSeparateStrips
+ * @param bRestart
+ * @param restartVal
+ */
+ int createStrips(final List<NvStripInfo> allStrips, final List<Integer> stripIndices, final boolean bStitchStrips,
+ final boolean bRestart, final int restartVal) {
+ int numSeparateStrips = 0;
+
+ NvFaceInfo tLastFace = new NvFaceInfo(0, 0, 0);
+ final int nStripCount = allStrips.size();
+ assert nStripCount > 0;
+
+ // we infer the cw/ccw ordering depending on the number of indices
+ // this is screwed up by the fact that we insert -1s to denote changing strips
+ // this is to account for that
+ int accountForNegatives = 0;
+
+ for (int i = 0; i < nStripCount; i++) {
+ final NvStripInfo strip = allStrips.get(i);
+ final int nStripFaceCount = strip._faces.size();
+ assert nStripFaceCount > 0;
+
+ // Handle the first face in the strip
+ {
+ final NvFaceInfo tFirstFace = new NvFaceInfo(strip._faces.get(0)._v0, strip._faces.get(0)._v1,
+ strip._faces.get(0)._v2);
+
+ // If there is a second face, reorder vertices such that the
+ // unique vertex is first
+ if (nStripFaceCount > 1) {
+ final int nUnique = NvStripifier.getUniqueVertexInB(strip._faces.get(1), tFirstFace);
+ if (nUnique == tFirstFace._v1) {
+ final int store = tFirstFace._v1;
+ tFirstFace._v1 = tFirstFace._v0;
+ tFirstFace._v0 = store;
+ } else if (nUnique == tFirstFace._v2) {
+ final int store = tFirstFace._v2;
+ tFirstFace._v2 = tFirstFace._v0;
+ tFirstFace._v0 = store;
+ }
+
+ // If there is a third face, reorder vertices such that the
+ // shared vertex is last
+ if (nStripFaceCount > 2) {
+ if (NvStripifier.isDegenerate(strip._faces.get(1))) {
+ final int pivot = strip._faces.get(1)._v1;
+ if (tFirstFace._v1 == pivot) {
+ final int store = tFirstFace._v2;
+ tFirstFace._v2 = tFirstFace._v1;
+ tFirstFace._v1 = store;
+ }
+ } else {
+ final int[] nShared = NvStripifier.getSharedVertices(strip._faces.get(2), tFirstFace);
+ if (nShared[0] == tFirstFace._v1 && nShared[1] == -1) {
+ final int store = tFirstFace._v2;
+ tFirstFace._v2 = tFirstFace._v1;
+ tFirstFace._v1 = store;
+ }
+ }
+ }
+ }
+
+ if (i == 0 || !bStitchStrips || bRestart) {
+ if (!NvStripifier.isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) {
+ stripIndices.add(tFirstFace._v0);
+ }
+ } else {
+ // Double tap the first in the new strip
+ stripIndices.add(tFirstFace._v0);
+
+ // Check CW/CCW ordering
+ if (NvStripifier.nextIsCW(stripIndices.size() - accountForNegatives) != NvStripifier.isCW(
+ strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) {
+ stripIndices.add(tFirstFace._v0);
+ }
+ }
+
+ stripIndices.add(tFirstFace._v0);
+ stripIndices.add(tFirstFace._v1);
+ stripIndices.add(tFirstFace._v2);
+
+ // Update last face info
+ tLastFace = tFirstFace;
+ }
+
+ for (int j = 1; j < nStripFaceCount; j++) {
+ final int nUnique = NvStripifier.getUniqueVertexInB(tLastFace, strip._faces.get(j));
+ if (nUnique != -1) {
+ stripIndices.add(nUnique);
+
+ // Update last face info
+ tLastFace._v0 = tLastFace._v1;
+ tLastFace._v1 = tLastFace._v2;
+ tLastFace._v2 = nUnique;
+ } else {
+ // we've hit a degenerate
+ stripIndices.add(strip._faces.get(j)._v2);
+ tLastFace._v0 = strip._faces.get(j)._v0;// tLastFace.m_v1;
+ tLastFace._v1 = strip._faces.get(j)._v1;// tLastFace.m_v2;
+ tLastFace._v2 = strip._faces.get(j)._v2;// tLastFace.m_v1;
+
+ }
+ }
+
+ // Double tap between strips.
+ if (bStitchStrips && !bRestart) {
+ if (i != nStripCount - 1) {
+ stripIndices.add(tLastFace._v2);
+ }
+ } else if (bRestart) {
+ stripIndices.add(restartVal);
+ } else {
+ // -1 index indicates next strip
+ stripIndices.add(-1);
+ accountForNegatives++;
+ numSeparateStrips++;
+ }
+
+ // Update last face info
+ tLastFace._v0 = tLastFace._v1;
+ tLastFace._v1 = tLastFace._v2;
+ // tLastFace._v2 = tLastFace._v2; // for info purposes.
+ }
+
+ if (bStitchStrips || bRestart) {
+ numSeparateStrips = 1;
+ }
+
+ return numSeparateStrips;
+ }
+
+ /**
+ * @param faceA
+ * @param faceB
+ * @return the first vertex unique to faceB
+ */
+ static int getUniqueVertexInB(final NvFaceInfo faceA, final NvFaceInfo faceB) {
+ final int facev0 = faceB._v0;
+ if (facev0 != faceA._v0 && facev0 != faceA._v1 && facev0 != faceA._v2) {
+ return facev0;
+ }
+
+ final int facev1 = faceB._v1;
+ if (facev1 != faceA._v0 && facev1 != faceA._v1 && facev1 != faceA._v2) {
+ return facev1;
+ }
+
+ final int facev2 = faceB._v2;
+ if (facev2 != faceA._v0 && facev2 != faceA._v1 && facev2 != faceA._v2) {
+ return facev2;
+ }
+
+ // nothing is different
+ return -1;
+ }
+
+ /**
+ * @param faceA
+ * @param faceB
+ * @return the (at most) two vertices shared between the two faces
+ */
+ static int[] getSharedVertices(final NvFaceInfo faceA, final NvFaceInfo faceB) {
+ final int[] vertexStore = new int[2];
+ vertexStore[0] = vertexStore[1] = -1;
+
+ final int facev0 = faceB._v0;
+ if (facev0 == faceA._v0 || facev0 == faceA._v1 || facev0 == faceA._v2) {
+ if (vertexStore[0] == -1) {
+ vertexStore[0] = facev0;
+ } else {
+ vertexStore[1] = facev0;
+ return vertexStore;
+ }
+ }
+
+ final int facev1 = faceB._v1;
+ if (facev1 == faceA._v0 || facev1 == faceA._v1 || facev1 == faceA._v2) {
+ if (vertexStore[0] == -1) {
+ vertexStore[0] = facev1;
+ } else {
+ vertexStore[1] = facev1;
+ return vertexStore;
+ }
+ }
+
+ final int facev2 = faceB._v2;
+ if (facev2 == faceA._v0 || facev2 == faceA._v1 || facev2 == faceA._v2) {
+ if (vertexStore[0] == -1) {
+ vertexStore[0] = facev2;
+ } else {
+ vertexStore[1] = facev2;
+ return vertexStore;
+ }
+ }
+
+ return vertexStore;
+ }
+
+ static boolean isDegenerate(final NvFaceInfo face) {
+ if (face._v0 == face._v1) {
+ return true;
+ } else if (face._v0 == face._v2) {
+ return true;
+ } else if (face._v1 == face._v2) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ static boolean isDegenerate(final int v0, final int v1, final int v2) {
+ if (v0 == v1) {
+ return true;
+ } else if (v0 == v2) {
+ return true;
+ } else if (v1 == v2) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////////////
+ //
+ // Big mess of functions called during stripification
+ // Note: I removed some that were orphans - JES
+ //
+ // ///////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @param faceInfo
+ * @param v0
+ * @param v1
+ * @return true if the face is ordered in CW fashion
+ */
+ static boolean isCW(final NvFaceInfo faceInfo, final int v0, final int v1) {
+ if (faceInfo._v0 == v0) {
+ return faceInfo._v1 == v1;
+ } else if (faceInfo._v1 == v0) {
+ return faceInfo._v2 == v1;
+ } else {
+ return faceInfo._v0 == v1;
+ }
+ }
+
+ /**
+ *
+ * @param numIndices
+ * @return true if the next face should be ordered in CW fashion
+ */
+ static boolean nextIsCW(final int numIndices) {
+ return numIndices % 2 == 0;
+ }
+
+ /**
+ * @param indices
+ * @param face
+ * @return vertex of the input face which is "next" in the input index list
+ */
+ static int getNextIndex(final List<Integer> indices, final NvFaceInfo face) {
+ final int numIndices = indices.size();
+ assert numIndices >= 2;
+
+ final int v0 = indices.get(numIndices - 2);
+ final int v1 = indices.get(numIndices - 1);
+
+ final int fv0 = face._v0;
+ final int fv1 = face._v1;
+ final int fv2 = face._v2;
+
+ if (fv0 != v0 && fv0 != v1) {
+ if (fv1 != v0 && fv1 != v1 || fv2 != v0 && fv2 != v1) {
+ NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n");
+ NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n");
+ }
+ return fv0;
+ }
+ if (fv1 != v0 && fv1 != v1) {
+ if (fv0 != v0 && fv0 != v1 || fv2 != v0 && fv2 != v1) {
+ NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n");
+ NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n");
+ }
+ return fv1;
+ }
+ if (fv2 != v0 && fv2 != v1) {
+ if (fv0 != v0 && fv0 != v1 || fv1 != v0 && fv1 != v1) {
+ NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n");
+ NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n");
+ }
+ return fv2;
+ }
+
+ // shouldn't get here, but let's try and fail gracefully
+ if (fv0 == fv1 || fv0 == fv2) {
+ return fv0;
+ } else if (fv1 == fv0 || fv1 == fv2) {
+ return fv1;
+ } else if (fv2 == fv0 || fv2 == fv1) {
+ return fv2;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * find the edge info for these two indices
+ *
+ * @param edgeInfos
+ * @param v0
+ * @param v1
+ * @return
+ */
+ static NvEdgeInfo findEdgeInfo(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1) {
+ // we can get to it through either array because the edge infos have a v0 and v1 and there is no order except
+ // how it was first created.
+ NvEdgeInfo infoIter = edgeInfos.get(v0);
+ while (infoIter != null) {
+ if (infoIter._v0 == v0) {
+ if (infoIter._v1 == v1) {
+ return infoIter;
+ } else {
+ infoIter = infoIter._nextV0;
+ }
+ } else {
+ assert infoIter._v1 == v0;
+ if (infoIter._v0 == v1) {
+ return infoIter;
+ } else {
+ infoIter = infoIter._nextV1;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * find the other face sharing these vertices
+ *
+ * @param edgeInfos
+ * @param v0
+ * @param v1
+ * @param faceInfo
+ * @return
+ */
+ static NvFaceInfo findOtherFace(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1,
+ final NvFaceInfo faceInfo) {
+ final NvEdgeInfo edgeInfo = NvStripifier.findEdgeInfo(edgeInfos, v0, v1);
+
+ if (edgeInfo == null && v0 == v1) {
+ // we've hit a degenerate
+ return null;
+ }
+
+ assert edgeInfo != null;
+ return edgeInfo._face0 == faceInfo ? edgeInfo._face1 : edgeInfo._face0;
+ }
+
+ /**
+ * A good reset point is one near other committed areas so that we know that when we've made the longest strips its
+ * because we're stripifying in the same general orientation.
+ *
+ * @param faceInfos
+ * @param edgeInfos
+ * @return
+ */
+ NvFaceInfo findGoodResetPoint(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos) {
+ // we hop into different areas of the mesh to try to get other large open spans done. Areas of small strips can
+ // just be left to triangle lists added at the end.
+ NvFaceInfo result = null;
+ final int numFaces = faceInfos.size();
+ int startPoint;
+ if (_firstTimeResetPoint) {
+ // first time, find a face with few neighbors (look for an edge of the mesh)
+ startPoint = findStartPoint(faceInfos, edgeInfos);
+ _firstTimeResetPoint = false;
+ } else {
+ startPoint = (int) (((float) numFaces - 1) * _meshJump);
+ }
+
+ if (startPoint == -1) {
+ startPoint = (int) (((float) numFaces - 1) * _meshJump);
+
+ // meshJump += 0.1f;
+ // if (meshJump > 1.0f)
+ // meshJump = .05f;
+ }
+
+ int i = startPoint;
+ do {
+
+ // if this guy isn't visited, try him
+ if (faceInfos.get(i)._stripId < 0) {
+ result = faceInfos.get(i);
+ break;
+ }
+
+ // update the index and clamp to 0-(numFaces-1)
+ if (++i >= numFaces) {
+ i = 0;
+ }
+
+ } while (i != startPoint);
+
+ // update the meshJump
+ _meshJump += 0.1f;
+ if (_meshJump > 1.0f) {
+ _meshJump = .05f;
+ }
+
+ // return the best face we found
+ return result;
+ }
+
+ /**
+ * Does the stripification, puts output strips into vector allStrips
+ *
+ * Works by setting running a number of experiments in different areas of the mesh, and accepting the one which
+ * results in the longest strips. It then accepts this, and moves on to a different area of the mesh. We try to jump
+ * around the mesh some, to ensure that large open spans of strips get generated.
+ *
+ * @param allStrips
+ * @param allFaceInfos
+ * @param allEdgeInfos
+ * @param numSamples
+ */
+ @SuppressWarnings("unchecked")
+ void findAllStrips(final List<NvStripInfo> allStrips, final List<NvFaceInfo> allFaceInfos,
+ final List<NvEdgeInfo> allEdgeInfos, final int numSamples) {
+ // the experiments
+ int experimentId = 0;
+ int stripId = 0;
+ boolean done = false;
+
+ while (!done) {
+
+ //
+ // PHASE 1: Set up numSamples * numEdges experiments
+ //
+ final List<NvStripInfo>[] experiments = new List[numSamples * 6];
+ for (int i = 0; i < experiments.length; i++) {
+ experiments[i] = Lists.newArrayList();
+ }
+
+ int experimentIndex = 0;
+ final Set<NvFaceInfo> resetPoints = Sets.newHashSet();
+ for (int i = 0; i < numSamples; i++) {
+
+ // Try to find another good reset point.
+ // If there are none to be found, we are done
+ final NvFaceInfo nextFace = findGoodResetPoint(allFaceInfos, allEdgeInfos);
+ if (nextFace == null) {
+ done = true;
+ break;
+ }
+ // If we have already evaluated starting at this face in this slew
+ // of experiments, then skip going any further
+ else if (resetPoints.contains(nextFace)) {
+ continue;
+ }
+
+ // trying it now...
+ resetPoints.add(nextFace);
+
+ // otherwise, we shall now try experiments for starting on the 01,12, and 20 edges
+ assert nextFace._stripId < 0;
+
+ // build the strip off of this face's 0-1 edge
+ final NvEdgeInfo edge01 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v0, nextFace._v1);
+ final NvStripInfo strip01 = new NvStripInfo(new NvStripStartInfo(nextFace, edge01, true), stripId++,
+ experimentId++);
+ experiments[experimentIndex++].add(strip01);
+
+ // build the strip off of this face's 1-0 edge
+ final NvEdgeInfo edge10 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v0, nextFace._v1);
+ final NvStripInfo strip10 = new NvStripInfo(new NvStripStartInfo(nextFace, edge10, false), stripId++,
+ experimentId++);
+ experiments[experimentIndex++].add(strip10);
+
+ // build the strip off of this face's 1-2 edge
+ final NvEdgeInfo edge12 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v1, nextFace._v2);
+ final NvStripInfo strip12 = new NvStripInfo(new NvStripStartInfo(nextFace, edge12, true), stripId++,
+ experimentId++);
+ experiments[experimentIndex++].add(strip12);
+
+ // build the strip off of this face's 2-1 edge
+ final NvEdgeInfo edge21 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v1, nextFace._v2);
+ final NvStripInfo strip21 = new NvStripInfo(new NvStripStartInfo(nextFace, edge21, false), stripId++,
+ experimentId++);
+ experiments[experimentIndex++].add(strip21);
+
+ // build the strip off of this face's 2-0 edge
+ final NvEdgeInfo edge20 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v2, nextFace._v0);
+ final NvStripInfo strip20 = new NvStripInfo(new NvStripStartInfo(nextFace, edge20, true), stripId++,
+ experimentId++);
+ experiments[experimentIndex++].add(strip20);
+
+ // build the strip off of this face's 0-2 edge
+ final NvEdgeInfo edge02 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v2, nextFace._v0);
+ final NvStripInfo strip02 = new NvStripInfo(new NvStripStartInfo(nextFace, edge02, false), stripId++,
+ experimentId++);
+ experiments[experimentIndex++].add(strip02);
+ }
+
+ //
+ // PHASE 2: Iterate through that we setup in the last phase
+ // and really build each of the strips and strips that follow to see how
+ // far we get
+ //
+ final int numExperiments = experimentIndex;
+ for (int i = 0; i < numExperiments; i++) {
+ // get the strip set
+ NvStripInfo stripIter = experiments[i].get(0);
+
+ // build the first strip of the list
+ stripIter.build(allEdgeInfos, allFaceInfos);
+ final int currExperimentId = stripIter._experimentId;
+
+ final NvStripStartInfo startInfo = new NvStripStartInfo(null, null, false);
+ while (findTraversal(allFaceInfos, allEdgeInfos, stripIter, startInfo)) {
+
+ // create the new strip info
+ stripIter = new NvStripInfo(startInfo, stripId++, currExperimentId);
+
+ // build the next strip
+ stripIter.build(allEdgeInfos, allFaceInfos);
+
+ // add it to the list
+ experiments[i].add(stripIter);
+ }
+ }
+
+ //
+ // Phase 3: Find the experiment that has the most promise
+ //
+ int bestIndex = 0;
+ double bestValue = 0;
+ for (int i = 0; i < numExperiments; i++) {
+ final float avgStripSizeWeight = 1.0f;
+ // final float numTrisWeight = 0.0f; // unused
+ final float numStripsWeight = 0.0f;
+ final float avgStripSize = avgStripSize(experiments[i]);
+ final float numStrips = experiments[i].size();
+ final float value = avgStripSize * avgStripSizeWeight + numStrips * numStripsWeight;
+ // float value = 1.f / numStrips;
+ // float value = numStrips * avgStripSize;
+
+ if (value > bestValue) {
+ bestValue = value;
+ bestIndex = i;
+ }
+ }
+
+ //
+ // Phase 4: commit the best experiment of the bunch
+ //
+ commitStrips(allStrips, experiments[bestIndex]);
+ }
+ }
+
+ /**
+ * Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then reorders these pieces
+ * to maximize cache hits. The final strips are stored in outStrips
+ *
+ * @param allStrips
+ * @param outStrips
+ * @param edgeInfos
+ * @param outFaceList
+ */
+ void splitUpStripsAndOptimize(final List<NvStripInfo> allStrips, final List<NvStripInfo> outStrips,
+ final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> outFaceList) {
+ final int threshold = _cacheSize;
+ final List<NvStripInfo> tempStrips = Lists.newArrayList();
+
+ // split up strips into threshold-sized pieces
+ for (int i = 0; i < allStrips.size(); i++) {
+ final NvStripInfo allStripI = allStrips.get(i);
+ NvStripInfo currentStrip;
+ final NvStripStartInfo startInfo = new NvStripStartInfo(null, null, false);
+
+ int actualStripSize = 0;
+ for (final NvFaceInfo face : allStripI._faces) {
+ if (!NvStripifier.isDegenerate(face)) {
+ actualStripSize++;
+ }
+ }
+
+ if (actualStripSize > threshold) {
+
+ final int numTimes = actualStripSize / threshold;
+ int numLeftover = actualStripSize % threshold;
+
+ int degenerateCount = 0, j = 0;
+ for (; j < numTimes; j++) {
+ currentStrip = new NvStripInfo(startInfo, 0, -1);
+
+ int faceCtr = j * threshold + degenerateCount;
+ boolean bFirstTime = true;
+ while (faceCtr < threshold + j * threshold + degenerateCount) {
+ if (NvStripifier.isDegenerate(allStripI._faces.get(faceCtr))) {
+ degenerateCount++;
+
+ // last time or first time through, no need for a degenerate
+ if ((faceCtr + 1 != threshold + j * threshold + degenerateCount || j == numTimes - 1
+ && numLeftover < 4 && numLeftover > 0)
+ && !bFirstTime) {
+ currentStrip._faces.add(allStripI._faces.get(faceCtr++));
+ } else {
+ ++faceCtr;
+ }
+ } else {
+ currentStrip._faces.add(allStripI._faces.get(faceCtr++));
+ bFirstTime = false;
+ }
+ }
+ if (j == numTimes - 1) // last time through
+ {
+ if (numLeftover < 4 && numLeftover > 0) // way too small
+ {
+ // just add to last strip
+ int ctr = 0;
+ while (ctr < numLeftover) {
+ if (NvStripifier.isDegenerate(allStripI._faces.get(faceCtr))) {
+ ++degenerateCount;
+ } else {
+ ++ctr;
+ }
+ currentStrip._faces.add(allStripI._faces.get(faceCtr++));
+ }
+ numLeftover = 0;
+ }
+ }
+ tempStrips.add(currentStrip);
+ }
+ int leftOff = j * threshold + degenerateCount;
+
+ if (numLeftover != 0) {
+ currentStrip = new NvStripInfo(startInfo, 0, -1);
+
+ int ctr = 0;
+ boolean bFirstTime = true;
+ while (ctr < numLeftover) {
+ if (!NvStripifier.isDegenerate(allStripI._faces.get(leftOff))) {
+ ctr++;
+ bFirstTime = false;
+ currentStrip._faces.add(allStripI._faces.get(leftOff++));
+ } else if (!bFirstTime) {
+ currentStrip._faces.add(allStripI._faces.get(leftOff++));
+ } else {
+ leftOff++;
+ }
+ }
+
+ tempStrips.add(currentStrip);
+ }
+ } else {
+ // we're not just doing a tempStrips.add(allBigStrips.get(i)) because
+ // this way we can delete allBigStrips later to free the memory
+ currentStrip = new NvStripInfo(startInfo, 0, -1);
+
+ for (int j = 0; j < allStripI._faces.size(); j++) {
+ currentStrip._faces.add(allStripI._faces.get(j));
+ }
+
+ tempStrips.add(currentStrip);
+ }
+ }
+
+ // add small strips to face list
+ final List<NvStripInfo> tempStrips2 = Lists.newArrayList();
+ removeSmallStrips(tempStrips, tempStrips2, outFaceList);
+
+ outStrips.clear();
+ // screw optimization for now
+ // for(i = 0; i < tempStrips.size(); ++i)
+ // outStrips.add(tempStrips.get(i));
+
+ if (tempStrips2.size() != 0) {
+ // Optimize for the vertex cache
+ final VertexCache vcache = new VertexCache(_cacheSize);
+
+ float bestNumHits = -1.0f;
+ float numHits;
+ int bestIndex = 0;
+ int firstIndex = 0;
+ float minCost = 10000.0f;
+
+ for (int i = 0; i < tempStrips2.size(); i++) {
+ final NvStripInfo tempStrips2I = tempStrips2.get(i);
+ int numNeighbors = 0;
+
+ // find strip with least number of neighbors per face
+ for (int j = 0; j < tempStrips2I._faces.size(); j++) {
+ numNeighbors += numNeighbors(tempStrips2I._faces.get(j), edgeInfos);
+ }
+
+ final float currCost = numNeighbors / (float) tempStrips2I._faces.size();
+ if (currCost < minCost) {
+ minCost = currCost;
+ firstIndex = i;
+ }
+ }
+
+ final NvStripInfo tempStrips2FirstIndex = tempStrips2.get(firstIndex);
+ updateCacheStrip(vcache, tempStrips2FirstIndex);
+ outStrips.add(tempStrips2FirstIndex);
+
+ tempStrips2FirstIndex._visited = true;
+
+ boolean bWantsCW = tempStrips2FirstIndex._faces.size() % 2 == 0;
+
+ // XXX: this n^2 algo is what slows down stripification so much.... needs to be improved
+ while (true) {
+ bestNumHits = -1.0f;
+
+ // find best strip to add next, given the current cache
+ for (int i = 0; i < tempStrips2.size(); i++) {
+ final NvStripInfo tempStrips2I = tempStrips2.get(i);
+ if (tempStrips2I._visited) {
+ continue;
+ }
+
+ numHits = calcNumHitsStrip(vcache, tempStrips2I);
+ if (numHits > bestNumHits) {
+ bestNumHits = numHits;
+ bestIndex = i;
+ } else if (numHits >= bestNumHits) {
+ // check previous strip to see if this one requires it to switch polarity
+ final NvStripInfo strip = tempStrips2I;
+ final int nStripFaceCount = strip._faces.size();
+
+ final NvFaceInfo tFirstFace = new NvFaceInfo(strip._faces.get(0));
+
+ // If there is a second face, reorder vertices such that the
+ // unique vertex is first
+ if (nStripFaceCount > 1) {
+ final int nUnique = NvStripifier.getUniqueVertexInB(strip._faces.get(1), tFirstFace);
+ if (nUnique == tFirstFace._v1) {
+ final int store = tFirstFace._v1;
+ tFirstFace._v1 = tFirstFace._v0;
+ tFirstFace._v0 = store;
+ } else if (nUnique == tFirstFace._v2) {
+ final int store = tFirstFace._v2;
+ tFirstFace._v2 = tFirstFace._v0;
+ tFirstFace._v0 = store;
+ }
+
+ // If there is a third face, reorder vertices such that the
+ // shared vertex is last
+ if (nStripFaceCount > 2) {
+ final int[] nShared = NvStripifier.getSharedVertices(strip._faces.get(2), tFirstFace);
+ if (nShared[0] == tFirstFace._v1 && nShared[1] == -1) {
+ final int store = tFirstFace._v2;
+ tFirstFace._v2 = tFirstFace._v1;
+ tFirstFace._v1 = store;
+ }
+ }
+ }
+
+ // Check CW/CCW ordering
+ if (bWantsCW == NvStripifier.isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) {
+ // I like this one!
+ bestIndex = i;
+ }
+ }
+ }
+
+ if (bestNumHits == -1.0f) {
+ break;
+ }
+ tempStrips2.get(bestIndex)._visited = true;
+ updateCacheStrip(vcache, tempStrips2.get(bestIndex));
+ outStrips.add(tempStrips2.get(bestIndex));
+ bWantsCW = tempStrips2.get(bestIndex)._faces.size() % 2 == 0 ? bWantsCW : !bWantsCW;
+ }
+ }
+ }
+
+ /**
+ * @param allStrips
+ * the whole strip vector...all small strips will be deleted from this list, to avoid leaking mem
+ * @param allBigStrips
+ * an out parameter which will contain all strips above minStripLength
+ * @param faceList
+ * an out parameter which will contain all faces which were removed from the striplist
+ */
+ void removeSmallStrips(final List<NvStripInfo> allStrips, final List<NvStripInfo> allBigStrips,
+ final List<NvFaceInfo> faceList) {
+ faceList.clear();
+ allBigStrips.clear(); // make sure these are empty
+ final List<NvFaceInfo> tempFaceList = Lists.newArrayList();
+
+ for (int i = 0; i < allStrips.size(); i++) {
+ final NvStripInfo allStripI = allStrips.get(i);
+ if (allStripI._faces.size() < _minStripLength) {
+ // strip is too small, add faces to faceList
+ for (int j = 0; j < allStripI._faces.size(); j++) {
+ tempFaceList.add(allStripI._faces.get(j));
+ }
+ } else {
+ allBigStrips.add(allStripI);
+ }
+ }
+
+ if (!tempFaceList.isEmpty()) {
+ final boolean[] bVisitedList = new boolean[tempFaceList.size()];
+ final VertexCache vcache = new VertexCache(_cacheSize);
+
+ int bestNumHits = -1;
+ int numHits = 0;
+ int bestIndex = 0;
+
+ while (true) {
+ bestNumHits = -1;
+
+ // find best face to add next, given the current cache
+ for (int i = 0; i < tempFaceList.size(); i++) {
+ if (bVisitedList[i]) {
+ continue;
+ }
+
+ numHits = calcNumHitsFace(vcache, tempFaceList.get(i));
+ if (numHits > bestNumHits) {
+ bestNumHits = numHits;
+ bestIndex = i;
+ }
+ }
+
+ if (bestNumHits == -1.0f) {
+ break;
+ }
+ bVisitedList[bestIndex] = true;
+ updateCacheFace(vcache, tempFaceList.get(bestIndex));
+ faceList.add(tempFaceList.get(bestIndex));
+ }
+ }
+ }
+
+ /**
+ * Finds the next face to start the next strip on.
+ *
+ * @param faceInfos
+ * @param edgeInfos
+ * @param strip
+ * @param startInfo
+ * @return
+ */
+ boolean findTraversal(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos, final NvStripInfo strip,
+ final NvStripStartInfo startInfo) {
+ // if the strip was v0.v1 on the edge, then v1 will be a vertex in the next edge.
+ final int v = strip._startInfo._toV1 ? strip._startInfo._startEdge._v1 : strip._startInfo._startEdge._v0;
+
+ NvFaceInfo untouchedFace = null;
+ NvEdgeInfo edgeIter = edgeInfos.get(v);
+ while (edgeIter != null) {
+ final NvFaceInfo face0 = edgeIter._face0;
+ final NvFaceInfo face1 = edgeIter._face1;
+ if (face0 != null && !strip.IsInStrip(face0) && face1 != null && !strip.isMarked(face1)) {
+ untouchedFace = face1;
+ break;
+ }
+ if (face1 != null && !strip.IsInStrip(face1) && face0 != null && !strip.isMarked(face0)) {
+ untouchedFace = face0;
+ break;
+ }
+
+ // find the next edgeIter
+ edgeIter = edgeIter._v0 == v ? edgeIter._nextV0 : edgeIter._nextV1;
+ }
+
+ startInfo._startFace = untouchedFace;
+ startInfo._startEdge = edgeIter;
+ if (edgeIter != null) {
+ if (strip.sharesEdge(startInfo._startFace, edgeInfos)) {
+ startInfo._toV1 = edgeIter._v0 == v; // note! used to be m_v1
+ } else {
+ startInfo._toV1 = edgeIter._v1 == v;
+ }
+ }
+ return startInfo._startFace != null;
+ }
+
+ /**
+ * "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips vector
+ *
+ * @param allStrips
+ * @param strips
+ */
+ void commitStrips(final List<NvStripInfo> allStrips, final List<NvStripInfo> strips) {
+ // Iterate through strips
+ for (final NvStripInfo strip : strips) {
+ // Tell the strip that it is now real
+ strip._experimentId = -1;
+
+ // add to the list of real strips
+ allStrips.add(strip);
+
+ // Iterate through the faces of the strip
+ // Tell the faces of the strip that they belong to a real strip now
+ final List<NvFaceInfo> faces = strip._faces;
+ for (final NvFaceInfo face : faces) {
+ strip.markTriangle(face);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param strips
+ * @return the average strip size of the input vector of strips
+ */
+ float avgStripSize(final List<NvStripInfo> strips) {
+ int sizeAccum = 0;
+ for (final NvStripInfo strip : strips) {
+ sizeAccum += strip._faces.size();
+ sizeAccum -= strip._numDegenerates;
+ }
+ return (float) sizeAccum / (float) strips.size();
+ }
+
+ /**
+ * Finds a good starting point, namely one which has only one neighbor
+ *
+ * @param faceInfos
+ * @param edgeInfos
+ * @return
+ */
+ int findStartPoint(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos) {
+ int bestCtr = -1;
+ int bestIndex = -1;
+
+ int i = 0;
+ for (final NvFaceInfo faceInfo : faceInfos) {
+ int ctr = 0;
+
+ if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v0, faceInfo._v1, faceInfo) == null) {
+ ctr++;
+ }
+ if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v1, faceInfo._v2, faceInfo) == null) {
+ ctr++;
+ }
+ if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v2, faceInfo._v0, faceInfo) == null) {
+ ctr++;
+ }
+ if (ctr > bestCtr) {
+ bestCtr = ctr;
+ bestIndex = i;
+ }
+ i++;
+ }
+
+ if (bestCtr == 0) {
+ return -1;
+ } else {
+ return bestIndex;
+ }
+ }
+
+ /**
+ * Updates the input vertex cache with this strip's vertices
+ *
+ * @param vcache
+ * @param strip
+ */
+ void updateCacheStrip(final VertexCache vcache, final NvStripInfo strip) {
+ for (final NvFaceInfo face : strip._faces) {
+ updateCacheFace(vcache, face);
+ }
+ }
+
+ /**
+ * Updates the input vertex cache with this face's vertices
+ *
+ * @param vcache
+ * @param face
+ */
+ void updateCacheFace(final VertexCache vcache, final NvFaceInfo face) {
+ if (!vcache.inCache(face._v0)) {
+ vcache.addEntry(face._v0);
+ }
+
+ if (!vcache.inCache(face._v1)) {
+ vcache.addEntry(face._v1);
+ }
+
+ if (!vcache.inCache(face._v2)) {
+ vcache.addEntry(face._v2);
+ }
+ }
+
+ /**
+ * @param vcache
+ * @param strip
+ * @return the number of cache hits per face in the strip
+ */
+ float calcNumHitsStrip(final VertexCache vcache, final NvStripInfo strip) {
+ int numHits = 0;
+ int numFaces = 0;
+
+ for (final NvFaceInfo face : strip._faces) {
+ if (vcache.inCache(face._v0)) {
+ ++numHits;
+ }
+
+ if (vcache.inCache(face._v1)) {
+ ++numHits;
+ }
+
+ if (vcache.inCache(face._v2)) {
+ ++numHits;
+ }
+
+ numFaces++;
+ }
+
+ return (float) numHits / (float) numFaces;
+ }
+
+ /**
+ * @param vcache
+ * @param face
+ * @return the number of cache hits in the face
+ */
+ int calcNumHitsFace(final VertexCache vcache, final NvFaceInfo face) {
+ int numHits = 0;
+
+ if (vcache.inCache(face._v0)) {
+ numHits++;
+ }
+
+ if (vcache.inCache(face._v1)) {
+ numHits++;
+ }
+
+ if (vcache.inCache(face._v2)) {
+ numHits++;
+ }
+
+ return numHits;
+ }
+
+ /**
+ *
+ * @param face
+ * @param edgeInfoVec
+ * @return the number of neighbors that this face has
+ */
+ int numNeighbors(final NvFaceInfo face, final List<NvEdgeInfo> edgeInfoVec) {
+ int numNeighbors = 0;
+
+ if (NvStripifier.findOtherFace(edgeInfoVec, face._v0, face._v1, face) != null) {
+ numNeighbors++;
+ }
+
+ if (NvStripifier.findOtherFace(edgeInfoVec, face._v1, face._v2, face) != null) {
+ numNeighbors++;
+ }
+
+ if (NvStripifier.findOtherFace(edgeInfoVec, face._v2, face._v0, face) != null) {
+ numNeighbors++;
+ }
+
+ return numNeighbors;
+ }
+
+ /**
+ * Builds the list of all face and edge infos
+ *
+ * @param faceInfos
+ * @param edgeInfos
+ * @param maxIndex
+ */
+ void buildStripifyInfo(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos, final int maxIndex) {
+ // reserve space for the face infos, but do not resize them.
+ final int numIndices = _indices.size();
+
+ // we actually resize the edge infos, so we must initialize to null
+ for (int i = 0; i <= maxIndex; i++) {
+ edgeInfos.add(null);
+ }
+
+ // iterate through the triangles of the triangle list
+ final int numTriangles = numIndices / 3;
+ int index = 0;
+ final boolean[] bFaceUpdated = new boolean[3];
+
+ for (int i = 0; i < numTriangles; i++) {
+ boolean bMightAlreadyExist = true;
+ bFaceUpdated[0] = false;
+ bFaceUpdated[1] = false;
+ bFaceUpdated[2] = false;
+
+ // grab the indices
+ final int v0 = _indices.get(index++);
+ final int v1 = _indices.get(index++);
+ final int v2 = _indices.get(index++);
+
+ // we disregard degenerates
+ if (NvStripifier.isDegenerate(v0, v1, v2)) {
+ continue;
+ }
+
+ // create the face info and add it to the list of faces, but only if this exact face doesn't already
+ // exist in the list
+ final NvFaceInfo faceInfo = new NvFaceInfo(v0, v1, v2);
+
+ // grab the edge infos, creating them if they do not already exist
+ NvEdgeInfo edgeInfo01 = NvStripifier.findEdgeInfo(edgeInfos, v0, v1);
+ if (edgeInfo01 == null) {
+ // since one of it's edges isn't in the edge data structure, it can't already exist in the face
+ // structure
+ bMightAlreadyExist = false;
+
+ // create the info
+ edgeInfo01 = new NvEdgeInfo(v0, v1);
+
+ // update the linked list on both
+ edgeInfo01._nextV0 = edgeInfos.get(v0);
+ edgeInfo01._nextV1 = edgeInfos.get(v1);
+ edgeInfos.set(v0, edgeInfo01);
+ edgeInfos.set(v1, edgeInfo01);
+
+ // set face 0
+ edgeInfo01._face0 = faceInfo;
+ } else {
+ if (edgeInfo01._face1 != null) {
+ NvStripifier.logger
+ .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
+ } else {
+ edgeInfo01._face1 = faceInfo;
+ bFaceUpdated[0] = true;
+ }
+ }
+
+ // grab the edge infos, creating them if they do not already exist
+ NvEdgeInfo edgeInfo12 = NvStripifier.findEdgeInfo(edgeInfos, v1, v2);
+ if (edgeInfo12 == null) {
+ bMightAlreadyExist = false;
+
+ // create the info
+ edgeInfo12 = new NvEdgeInfo(v1, v2);
+
+ // update the linked list on both
+ edgeInfo12._nextV0 = edgeInfos.get(v1);
+ edgeInfo12._nextV1 = edgeInfos.get(v2);
+ edgeInfos.set(v1, edgeInfo12);
+ edgeInfos.set(v2, edgeInfo12);
+
+ // set face 0
+ edgeInfo12._face0 = faceInfo;
+ } else {
+ if (edgeInfo12._face1 != null) {
+ NvStripifier.logger
+ .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
+ } else {
+ edgeInfo12._face1 = faceInfo;
+ bFaceUpdated[1] = true;
+ }
+ }
+
+ // grab the edge infos, creating them if they do not already exist
+ NvEdgeInfo edgeInfo20 = NvStripifier.findEdgeInfo(edgeInfos, v2, v0);
+ if (edgeInfo20 == null) {
+ bMightAlreadyExist = false;
+
+ // create the info
+ edgeInfo20 = new NvEdgeInfo(v2, v0);
+
+ // update the linked list on both
+ edgeInfo20._nextV0 = edgeInfos.get(v2);
+ edgeInfo20._nextV1 = edgeInfos.get(v0);
+ edgeInfos.set(v2, edgeInfo20);
+ edgeInfos.set(v0, edgeInfo20);
+
+ // set face 0
+ edgeInfo20._face0 = faceInfo;
+ } else {
+ if (edgeInfo20._face1 != null) {
+ NvStripifier.logger
+ .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n");
+ } else {
+ edgeInfo20._face1 = faceInfo;
+ bFaceUpdated[2] = true;
+ }
+ }
+
+ if (bMightAlreadyExist) {
+ if (!alreadyExists(faceInfo, faceInfos)) {
+ faceInfos.add(faceInfo);
+ } else {
+ // cleanup pointers that point to this deleted face
+ if (bFaceUpdated[0]) {
+ edgeInfo01._face1 = null;
+ }
+ if (bFaceUpdated[1]) {
+ edgeInfo12._face1 = null;
+ }
+ if (bFaceUpdated[2]) {
+ edgeInfo20._face1 = null;
+ }
+ }
+ } else {
+ faceInfos.add(faceInfo);
+ }
+ }
+ }
+
+ boolean alreadyExists(final NvFaceInfo toFind, final List<NvFaceInfo> faceInfos) {
+ for (final NvFaceInfo face : faceInfos) {
+ if (face._v0 == toFind._v0 && face._v1 == toFind._v1 && face._v2 == toFind._v2) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java
index c16a631..c12d86d 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/NvTriangleStripper.java
@@ -1,564 +1,564 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-import com.ardor3d.renderer.IndexMode;
-import com.ardor3d.scenegraph.IndexBufferData;
-import com.ardor3d.scenegraph.IntBufferData;
-import com.ardor3d.scenegraph.Mesh;
-import com.ardor3d.scenegraph.MeshData;
-import com.ardor3d.scenegraph.Spatial;
-import com.ardor3d.scenegraph.visitor.Visitor;
-import com.ardor3d.util.geom.BufferUtils;
-import com.google.common.collect.Lists;
-
-/**
- * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
- */
-public class NvTriangleStripper implements Visitor {
- /** GeForce1 and 2 cache size */
- public static final int CACHESIZE_GEFORCE1_2 = 16;
-
- /** GeForce3 cache size */
- public static final int CACHESIZE_GEFORCE3 = 24;
-
- private int _cacheSize = NvTriangleStripper.CACHESIZE_GEFORCE3;
- private int _minStripSize = 0;
- private int _restartVal = 0;
- private boolean _stitchStrips = true;
- private boolean _listsOnly = false;
- private boolean _restart = false;
- private boolean _reorderVertices = false;
-
- /**
- * For GPUs that support primitive restart, this sets a value as the restart index
- *
- * Restart is meaningless if strips are not being stitched together, so enabling restart makes NvTriStrip forcing
- * stitching. So, you'll get back one strip.
- *
- * @param restartVal
- */
- public void enableRestart(final int restartVal) {
- _restart = true;
- _restartVal = restartVal;
- }
-
- /**
- * For GPUs that support primitive restart, this disables using primitive restart
- */
- public void disableRestart() {
- _restart = false;
- }
-
- public boolean isRestart() {
- return _restart;
- }
-
- /**
- * Sets the cache size which the stripfier uses to optimize the data. Controls the length of the generated
- * individual strips. This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2 You may want to play
- * around with this number to tweak performance.
- *
- * @param cacheSize
- * (Default value: 24)
- */
- public void setCacheSize(final int cacheSize) {
- _cacheSize = cacheSize;
- }
-
- public int getCacheSize() {
- return _cacheSize;
- }
-
- /**
- * boolean to indicate whether to stitch together strips into one huge strip or not. If set to true, you'll get back
- * one huge strip stitched together using degenerate triangles. If set to false, you'll get back a large number of
- * separate strips.
- *
- * @param bStitchStrips
- * (Default value: true)
- */
- public void setStitchStrips(final boolean bStitchStrips) {
- _stitchStrips = bStitchStrips;
- }
-
- public boolean isStitchStrips() {
- return _stitchStrips;
- }
-
- /**
- *
- * Sets the minimum acceptable size for a strip, in triangles. All strips generated which are shorter than this will
- * be thrown into one big, separate list.
- *
- * @param minSize
- * (Default value: 0)
- */
- public void setMinStripSize(final int minSize) {
- _minStripSize = minSize;
- }
-
- public int getMinStripSize() {
- return _minStripSize;
- }
-
- /**
- * If set to true, will return an optimized list, with no strips at all.
- *
- * @param bListsOnly
- * (Default value: false)
- */
- public void setListsOnly(final boolean bListsOnly) {
- _listsOnly = bListsOnly;
- }
-
- public boolean isListsOnly() {
- return _listsOnly;
- }
-
- /**
- * If set to true, will call remapIndices after generateStrips.
- *
- * @param reorder
- * (Default value: false)
- * @see #remapIndices(PrimitiveGroup[], AtomicReference, int)
- */
- public void setReorderVertices(final boolean reorder) {
- _reorderVertices = reorder;
- }
-
- public boolean isReorderVertices() {
- return _reorderVertices;
- }
-
- /**
- *
- Returns true if the two triangles defined by firstTri and secondTri are the same The "same" is defined in this
- * case as having the same indices with the same winding order
- *
- * @param firstTri0
- * @param firstTri1
- * @param firstTri2
- * @param secondTri0
- * @param secondTri1
- * @param secondTri2
- * @return
- */
- boolean sameTriangle(final int firstTri0, final int firstTri1, final int firstTri2, final int secondTri0,
- final int secondTri1, final int secondTri2) {
- boolean isSame = false;
-
- if (firstTri0 == secondTri0) {
- if (firstTri1 == secondTri1) {
- if (firstTri2 == secondTri2) {
- isSame = true;
- }
- }
- } else if (firstTri0 == secondTri1) {
- if (firstTri1 == secondTri2) {
- if (firstTri2 == secondTri0) {
- isSame = true;
- }
- }
- } else if (firstTri0 == secondTri2) {
- if (firstTri1 == secondTri0) {
- if (firstTri2 == secondTri1) {
- isSame = true;
- }
- }
- }
-
- return isSame;
- }
-
- boolean testTriangle(final int v0, final int v1, final int v2, final List<NvFaceInfo> in_bins[], final int NUMBINS) {
- // hash this triangle
- boolean isLegit = false;
- int ctr = v0 % NUMBINS;
- NvFaceInfo face;
- for (int k = 0; k < in_bins[ctr].size(); ++k) {
- // check triangles in this bin
- face = in_bins[ctr].get(k);
- if (sameTriangle(face._v0, face._v1, face._v2, v0, v1, v2)) {
- isLegit = true;
- break;
- }
- }
- if (!isLegit) {
- ctr = v1 % NUMBINS;
- for (int k = 0; k < in_bins[ctr].size(); ++k) {
- face = in_bins[ctr].get(k);
- // check triangles in this bin
- if (sameTriangle(face._v0, face._v1, face._v2, v0, v1, v2)) {
- isLegit = true;
- break;
- }
- }
-
- if (!isLegit) {
- ctr = v2 % NUMBINS;
- for (int k = 0; k < in_bins[ctr].size(); ++k) {
- face = in_bins[ctr].get(k);
- // check triangles in this bin
- if (sameTriangle(face._v0, face._v1, face._v2, v0, v1, v2)) {
- isLegit = true;
- break;
- }
- }
-
- }
- }
-
- return isLegit;
- }
-
- /**
- *
- * @param in_indices
- * input index list, the indices you would use to render
- * @param validate
- * @return array of optimized/stripified PrimitiveGroups
- */
- @SuppressWarnings("unchecked")
- public PrimitiveGroup[] generateStrips(final int[] in_indices, final boolean validate) {
- if (in_indices == null || in_indices.length == 0) {
- return new PrimitiveGroup[0];
- }
-
- int numGroups = 0;
- PrimitiveGroup[] primGroups;
-
- // put data in format that the stripifier likes
- final List<Integer> tempIndices = Lists.newArrayList();
- int maxIndex = 0;
- for (int i = 0; i < in_indices.length; i++) {
- tempIndices.add(in_indices[i]);
- if (in_indices[i] > maxIndex) {
- maxIndex = in_indices[i];
- }
- }
- final List<NvStripInfo> tempStrips = Lists.newArrayList();
- final List<NvFaceInfo> tempFaces = Lists.newArrayList();
-
- final NvStripifier stripifier = new NvStripifier();
-
- // do actual stripification
- stripifier.stripify(tempIndices, _cacheSize, _minStripSize, maxIndex, tempStrips, tempFaces);
-
- // stitch strips together
- final List<Integer> stripIndices = Lists.newArrayList();
- int numSeparateStrips = 0;
-
- if (_listsOnly) {
- // if we're outputting only lists, we're done
- numGroups = 1;
- primGroups = new PrimitiveGroup[numGroups];
- primGroups[0] = new PrimitiveGroup();
- final PrimitiveGroup[] primGroupArray = primGroups;
-
- // count the total number of indices
- int numIndices = 0;
- for (int i = 0; i < tempStrips.size(); i++) {
- numIndices += tempStrips.get(i)._faces.size() * 3;
- }
-
- // add in the list
- numIndices += tempFaces.size() * 3;
-
- primGroupArray[0].setType(IndexMode.Triangles);
- primGroupArray[0].setIndices(new int[numIndices]);
- primGroupArray[0].setNumIndices(numIndices);
-
- // do strips
- int indexCtr = 0;
- for (final NvStripInfo strip : tempStrips) {
- for (final NvFaceInfo face : strip._faces) {
- // degenerates are of no use with lists
- if (!NvStripifier.isDegenerate(face)) {
- primGroupArray[0]._getIndices()[indexCtr++] = face._v0;
- primGroupArray[0]._getIndices()[indexCtr++] = face._v1;
- primGroupArray[0]._getIndices()[indexCtr++] = face._v2;
- } else {
- // we've removed a tri, reduce the number of indices
- primGroupArray[0].setNumIndices(primGroupArray[0].getNumIndices() - 3);
- }
- }
- }
-
- // do lists
- for (final NvFaceInfo face : tempFaces) {
- primGroupArray[0]._getIndices()[indexCtr++] = face._v0;
- primGroupArray[0]._getIndices()[indexCtr++] = face._v1;
- primGroupArray[0]._getIndices()[indexCtr++] = face._v2;
- }
- } else {
- numSeparateStrips = stripifier.createStrips(tempStrips, stripIndices, _stitchStrips, _restart, _restartVal);
-
- // if we're stitching strips together, we better get back only one strip from createStrips()
- assert _stitchStrips && numSeparateStrips == 1 || !_stitchStrips;
-
- // convert to output format
- numGroups = numSeparateStrips; // for the strips
- if (tempFaces.size() != 0) {
- numGroups++;
- } // we've got a list as well, increment
- primGroups = new PrimitiveGroup[numGroups];
- for (int i = 0; i < primGroups.length; i++) {
- primGroups[i] = new PrimitiveGroup();
- }
- final PrimitiveGroup[] primGroupArray = primGroups;
-
- // first, the strips
- int startingLoc = 0;
- for (int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++) {
- int stripLength = 0;
-
- if (!_stitchStrips) {
- int i = startingLoc;
- // if we've got multiple strips, we need to figure out the correct length
- for (; i < stripIndices.size(); i++) {
- if (stripIndices.get(i) == -1) {
- break;
- }
- }
-
- stripLength = i - startingLoc;
- } else {
- stripLength = stripIndices.size();
- }
-
- primGroupArray[stripCtr].setType(IndexMode.TriangleStrip);
- primGroupArray[stripCtr].setIndices(new int[stripLength]);
- primGroupArray[stripCtr].setNumIndices(stripLength);
-
- int indexCtr = 0;
- for (int i = startingLoc; i < stripLength + startingLoc; i++) {
- primGroupArray[stripCtr]._getIndices()[indexCtr++] = stripIndices.get(i);
- }
-
- // we add 1 to account for the -1 separating strips
- // this doesn't break the stitched case since we'll exit the loop
- startingLoc += stripLength + 1;
- }
-
- // next, the list
- if (tempFaces.size() != 0) {
- final int faceGroupLoc = numGroups - 1; // the face group is the last one
- primGroupArray[faceGroupLoc].setType(IndexMode.Triangles);
- primGroupArray[faceGroupLoc].setIndices(new int[tempFaces.size() * 3]);
- primGroupArray[faceGroupLoc].setNumIndices(tempFaces.size() * 3);
- int indexCtr = 0;
- for (final NvFaceInfo face : tempFaces) {
- primGroupArray[faceGroupLoc]._getIndices()[indexCtr++] = face._v0;
- primGroupArray[faceGroupLoc]._getIndices()[indexCtr++] = face._v1;
- primGroupArray[faceGroupLoc]._getIndices()[indexCtr++] = face._v2;
- }
- }
- }
-
- // validate generated data against input
- if (validate) {
- final int NUMBINS = 100;
-
- final List<NvFaceInfo> in_bins[] = new List[NUMBINS];
- for (int i = 0; i < NUMBINS; i++) {
- in_bins[i] = Lists.newArrayList();
- }
-
- // hash input indices on first index
- for (int i = 0; i < in_indices.length; i += 3) {
- final NvFaceInfo faceInfo = new NvFaceInfo(in_indices[i], in_indices[i + 1], in_indices[i + 2]);
- in_bins[in_indices[i] % NUMBINS].add(faceInfo);
- }
-
- for (int i = 0; i < numGroups; ++i) {
- switch (primGroups[i].getType()) {
- case Triangles: {
- for (int j = 0; j < primGroups[i].getNumIndices(); j += 3) {
- final int v0 = primGroups[i]._getIndices()[j];
- final int v1 = primGroups[i]._getIndices()[j + 1];
- final int v2 = primGroups[i]._getIndices()[j + 2];
-
- // ignore degenerates
- if (NvStripifier.isDegenerate(v0, v1, v2)) {
- continue;
- }
-
- if (!testTriangle(v0, v1, v2, in_bins, NUMBINS)) {
- throw new IllegalStateException("failed validation");
- }
- }
- break;
- }
-
- case TriangleStrip: {
- boolean flip = false;
- for (int j = 2; j < primGroups[i].getNumIndices(); ++j) {
- final int v0 = primGroups[i]._getIndices()[j - 2];
- int v1 = primGroups[i]._getIndices()[j - 1];
- int v2 = primGroups[i]._getIndices()[j];
-
- if (flip) {
- // swap v1 and v2
- final int swap = v1;
- v1 = v2;
- v2 = swap;
- }
-
- // ignore degenerates
- if (NvStripifier.isDegenerate(v0, v1, v2)) {
- flip = !flip;
- continue;
- }
-
- if (!testTriangle(v0, v1, v2, in_bins, NUMBINS)) {
- throw new IllegalStateException("failed validation");
- }
-
- flip = !flip;
- }
- break;
- }
-
- case TriangleFan:
- default:
- break;
- }
- }
-
- }
-
- return primGroups;
- }
-
- /**
- * Function to remap your indices to improve spatial locality in your vertex buffer.
- *
- * Note that you must reorder your vertex buffer according to the remapping handed back to you.
- *
- * Credit goes to the MS Xbox crew for the idea for this interface.
- *
- * @param in_primGroups
- * array of PrimitiveGroups you want remapped
- * @param numVerts
- * number of vertices in your vertex buffer, also can be thought of as the range of acceptable values for
- * indices in your primitive groups.
- * @return index remap. old index is key into array, value there is the old location for the vertex. -1 means vertex
- * was never referenced
- */
- public PrimitiveGroup[] remapIndices(final PrimitiveGroup[] in_primGroups,
- final AtomicReference<int[]> remappedVertices, final int numVerts) {
- final PrimitiveGroup[] remappedGroups = new PrimitiveGroup[in_primGroups.length];
-
- // caches oldIndex --> newIndex conversion
- final int[] indexCache = new int[numVerts];
- Arrays.fill(indexCache, -1);
-
- // loop over primitive groups
- int indexCtr = 0;
- for (int i = 0; i < in_primGroups.length; i++) {
- final int numIndices = in_primGroups[i].getNumIndices();
-
- // init remapped group
- remappedGroups[i] = new PrimitiveGroup();
- remappedGroups[i].setType(in_primGroups[i].getType());
- remappedGroups[i].setNumIndices(numIndices);
- remappedGroups[i].setIndices(new int[numIndices]);
-
- for (int j = 0; j < numIndices; j++) {
- final int cachedIndex = indexCache[in_primGroups[i]._getIndices()[j]];
- if (cachedIndex == -1) // we haven't seen this index before
- {
- // point to "last" vertex in VB
- remappedGroups[i]._getIndices()[j] = indexCtr;
-
- // add to index cache, increment
- indexCache[in_primGroups[i]._getIndices()[j]] = indexCtr++;
- } else {
- // we've seen this index before
- remappedGroups[i]._getIndices()[j] = cachedIndex;
- }
- }
- }
- if (remappedVertices != null) {
- remappedVertices.set(indexCache);
- }
-
- return remappedGroups;
- }
-
- @Override
- public void visit(final Spatial spatial) {
- if (spatial instanceof Mesh) {
- final Mesh mesh = (Mesh) spatial;
- final MeshData md = mesh.getMeshData();
- if (md.getTotalPrimitiveCount() < 1 || md.getVertexCount() < 3) {
- return;
- }
- for (final IndexMode mode : md.getIndexModes()) {
- if (mode != IndexMode.Triangles) {
- return;
- }
- }
-
- final int[] indices;
- if (md.getIndices() == null) {
- indices = new int[md.getVertexCount()];
- for (int i = 0; i < indices.length; i++) {
- indices[i] = i;
- }
- } else {
- indices = BufferUtils.getIntArray(md.getIndices());
- }
- PrimitiveGroup[] strips = generateStrips(indices, false);
-
- if (_reorderVertices) {
- final AtomicReference<int[]> newOrder = new AtomicReference<int[]>();
- strips = remapIndices(strips, newOrder, md.getVertexCount());
-
- // ask mesh to apply new vertex order
- mesh.reorderVertexData(newOrder.get());
- }
-
- // construct our new index buffer, modes and counts
- int indexCount = 0, j = 0, count = 0;
- for (final PrimitiveGroup group : strips) {
- if (group.getIndices().length > 0) {
- count++;
- }
- }
- final int[] counts = new int[count];
- final IndexMode[] modes = new IndexMode[count];
- for (final PrimitiveGroup group : strips) {
- indexCount += group.getIndices().length;
- if (group.getIndices().length > 0) {
- modes[j] = group.getType();
- counts[j++] = group.getIndices().length;
- }
- }
- final IndexBufferData<?> newIndices = BufferUtils.createIndexBufferData(indexCount, md.getVertexCount());
- for (final PrimitiveGroup group : strips) {
- final IntBufferData data = new IntBufferData(group.getIndices().length);
- data.getBuffer().put(group.getIndices());
- data.rewind();
- newIndices.put(data);
- }
- newIndices.rewind();
-
- // ask mesh to apply new index data
- mesh.reorderIndices(newIndices, modes, counts);
- }
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.ardor3d.renderer.IndexMode;
+import com.ardor3d.scenegraph.IndexBufferData;
+import com.ardor3d.scenegraph.IntBufferData;
+import com.ardor3d.scenegraph.Mesh;
+import com.ardor3d.scenegraph.MeshData;
+import com.ardor3d.scenegraph.Spatial;
+import com.ardor3d.scenegraph.visitor.Visitor;
+import com.ardor3d.util.geom.BufferUtils;
+import com.google.common.collect.Lists;
+
+/**
+ * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
+ */
+public class NvTriangleStripper implements Visitor {
+ /** GeForce1 and 2 cache size */
+ public static final int CACHESIZE_GEFORCE1_2 = 16;
+
+ /** GeForce3 cache size */
+ public static final int CACHESIZE_GEFORCE3 = 24;
+
+ private int _cacheSize = NvTriangleStripper.CACHESIZE_GEFORCE3;
+ private int _minStripSize = 0;
+ private int _restartVal = 0;
+ private boolean _stitchStrips = true;
+ private boolean _listsOnly = false;
+ private boolean _restart = false;
+ private boolean _reorderVertices = false;
+
+ /**
+ * For GPUs that support primitive restart, this sets a value as the restart index
+ *
+ * Restart is meaningless if strips are not being stitched together, so enabling restart makes NvTriStrip forcing
+ * stitching. So, you'll get back one strip.
+ *
+ * @param restartVal
+ */
+ public void enableRestart(final int restartVal) {
+ _restart = true;
+ _restartVal = restartVal;
+ }
+
+ /**
+ * For GPUs that support primitive restart, this disables using primitive restart
+ */
+ public void disableRestart() {
+ _restart = false;
+ }
+
+ public boolean isRestart() {
+ return _restart;
+ }
+
+ /**
+ * Sets the cache size which the stripfier uses to optimize the data. Controls the length of the generated
+ * individual strips. This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2 You may want to play
+ * around with this number to tweak performance.
+ *
+ * @param cacheSize
+ * (Default value: 24)
+ */
+ public void setCacheSize(final int cacheSize) {
+ _cacheSize = cacheSize;
+ }
+
+ public int getCacheSize() {
+ return _cacheSize;
+ }
+
+ /**
+ * boolean to indicate whether to stitch together strips into one huge strip or not. If set to true, you'll get back
+ * one huge strip stitched together using degenerate triangles. If set to false, you'll get back a large number of
+ * separate strips.
+ *
+ * @param bStitchStrips
+ * (Default value: true)
+ */
+ public void setStitchStrips(final boolean bStitchStrips) {
+ _stitchStrips = bStitchStrips;
+ }
+
+ public boolean isStitchStrips() {
+ return _stitchStrips;
+ }
+
+ /**
+ *
+ * Sets the minimum acceptable size for a strip, in triangles. All strips generated which are shorter than this will
+ * be thrown into one big, separate list.
+ *
+ * @param minSize
+ * (Default value: 0)
+ */
+ public void setMinStripSize(final int minSize) {
+ _minStripSize = minSize;
+ }
+
+ public int getMinStripSize() {
+ return _minStripSize;
+ }
+
+ /**
+ * If set to true, will return an optimized list, with no strips at all.
+ *
+ * @param bListsOnly
+ * (Default value: false)
+ */
+ public void setListsOnly(final boolean bListsOnly) {
+ _listsOnly = bListsOnly;
+ }
+
+ public boolean isListsOnly() {
+ return _listsOnly;
+ }
+
+ /**
+ * If set to true, will call remapIndices after generateStrips.
+ *
+ * @param reorder
+ * (Default value: false)
+ * @see #remapIndices(PrimitiveGroup[], AtomicReference, int)
+ */
+ public void setReorderVertices(final boolean reorder) {
+ _reorderVertices = reorder;
+ }
+
+ public boolean isReorderVertices() {
+ return _reorderVertices;
+ }
+
+ /**
+ *
+ Returns true if the two triangles defined by firstTri and secondTri are the same The "same" is defined in this
+ * case as having the same indices with the same winding order
+ *
+ * @param firstTri0
+ * @param firstTri1
+ * @param firstTri2
+ * @param secondTri0
+ * @param secondTri1
+ * @param secondTri2
+ * @return
+ */
+ boolean sameTriangle(final int firstTri0, final int firstTri1, final int firstTri2, final int secondTri0,
+ final int secondTri1, final int secondTri2) {
+ boolean isSame = false;
+
+ if (firstTri0 == secondTri0) {
+ if (firstTri1 == secondTri1) {
+ if (firstTri2 == secondTri2) {
+ isSame = true;
+ }
+ }
+ } else if (firstTri0 == secondTri1) {
+ if (firstTri1 == secondTri2) {
+ if (firstTri2 == secondTri0) {
+ isSame = true;
+ }
+ }
+ } else if (firstTri0 == secondTri2) {
+ if (firstTri1 == secondTri0) {
+ if (firstTri2 == secondTri1) {
+ isSame = true;
+ }
+ }
+ }
+
+ return isSame;
+ }
+
+ boolean testTriangle(final int v0, final int v1, final int v2, final List<NvFaceInfo> in_bins[], final int NUMBINS) {
+ // hash this triangle
+ boolean isLegit = false;
+ int ctr = v0 % NUMBINS;
+ NvFaceInfo face;
+ for (int k = 0; k < in_bins[ctr].size(); ++k) {
+ // check triangles in this bin
+ face = in_bins[ctr].get(k);
+ if (sameTriangle(face._v0, face._v1, face._v2, v0, v1, v2)) {
+ isLegit = true;
+ break;
+ }
+ }
+ if (!isLegit) {
+ ctr = v1 % NUMBINS;
+ for (int k = 0; k < in_bins[ctr].size(); ++k) {
+ face = in_bins[ctr].get(k);
+ // check triangles in this bin
+ if (sameTriangle(face._v0, face._v1, face._v2, v0, v1, v2)) {
+ isLegit = true;
+ break;
+ }
+ }
+
+ if (!isLegit) {
+ ctr = v2 % NUMBINS;
+ for (int k = 0; k < in_bins[ctr].size(); ++k) {
+ face = in_bins[ctr].get(k);
+ // check triangles in this bin
+ if (sameTriangle(face._v0, face._v1, face._v2, v0, v1, v2)) {
+ isLegit = true;
+ break;
+ }
+ }
+
+ }
+ }
+
+ return isLegit;
+ }
+
+ /**
+ *
+ * @param in_indices
+ * input index list, the indices you would use to render
+ * @param validate
+ * @return array of optimized/stripified PrimitiveGroups
+ */
+ @SuppressWarnings("unchecked")
+ public PrimitiveGroup[] generateStrips(final int[] in_indices, final boolean validate) {
+ if (in_indices == null || in_indices.length == 0) {
+ return new PrimitiveGroup[0];
+ }
+
+ int numGroups = 0;
+ PrimitiveGroup[] primGroups;
+
+ // put data in format that the stripifier likes
+ final List<Integer> tempIndices = Lists.newArrayList();
+ int maxIndex = 0;
+ for (int i = 0; i < in_indices.length; i++) {
+ tempIndices.add(in_indices[i]);
+ if (in_indices[i] > maxIndex) {
+ maxIndex = in_indices[i];
+ }
+ }
+ final List<NvStripInfo> tempStrips = Lists.newArrayList();
+ final List<NvFaceInfo> tempFaces = Lists.newArrayList();
+
+ final NvStripifier stripifier = new NvStripifier();
+
+ // do actual stripification
+ stripifier.stripify(tempIndices, _cacheSize, _minStripSize, maxIndex, tempStrips, tempFaces);
+
+ // stitch strips together
+ final List<Integer> stripIndices = Lists.newArrayList();
+ int numSeparateStrips = 0;
+
+ if (_listsOnly) {
+ // if we're outputting only lists, we're done
+ numGroups = 1;
+ primGroups = new PrimitiveGroup[numGroups];
+ primGroups[0] = new PrimitiveGroup();
+ final PrimitiveGroup[] primGroupArray = primGroups;
+
+ // count the total number of indices
+ int numIndices = 0;
+ for (int i = 0; i < tempStrips.size(); i++) {
+ numIndices += tempStrips.get(i)._faces.size() * 3;
+ }
+
+ // add in the list
+ numIndices += tempFaces.size() * 3;
+
+ primGroupArray[0].setType(IndexMode.Triangles);
+ primGroupArray[0].setIndices(new int[numIndices]);
+ primGroupArray[0].setNumIndices(numIndices);
+
+ // do strips
+ int indexCtr = 0;
+ for (final NvStripInfo strip : tempStrips) {
+ for (final NvFaceInfo face : strip._faces) {
+ // degenerates are of no use with lists
+ if (!NvStripifier.isDegenerate(face)) {
+ primGroupArray[0]._getIndices()[indexCtr++] = face._v0;
+ primGroupArray[0]._getIndices()[indexCtr++] = face._v1;
+ primGroupArray[0]._getIndices()[indexCtr++] = face._v2;
+ } else {
+ // we've removed a tri, reduce the number of indices
+ primGroupArray[0].setNumIndices(primGroupArray[0].getNumIndices() - 3);
+ }
+ }
+ }
+
+ // do lists
+ for (final NvFaceInfo face : tempFaces) {
+ primGroupArray[0]._getIndices()[indexCtr++] = face._v0;
+ primGroupArray[0]._getIndices()[indexCtr++] = face._v1;
+ primGroupArray[0]._getIndices()[indexCtr++] = face._v2;
+ }
+ } else {
+ numSeparateStrips = stripifier.createStrips(tempStrips, stripIndices, _stitchStrips, _restart, _restartVal);
+
+ // if we're stitching strips together, we better get back only one strip from createStrips()
+ assert _stitchStrips && numSeparateStrips == 1 || !_stitchStrips;
+
+ // convert to output format
+ numGroups = numSeparateStrips; // for the strips
+ if (tempFaces.size() != 0) {
+ numGroups++;
+ } // we've got a list as well, increment
+ primGroups = new PrimitiveGroup[numGroups];
+ for (int i = 0; i < primGroups.length; i++) {
+ primGroups[i] = new PrimitiveGroup();
+ }
+ final PrimitiveGroup[] primGroupArray = primGroups;
+
+ // first, the strips
+ int startingLoc = 0;
+ for (int stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++) {
+ int stripLength = 0;
+
+ if (!_stitchStrips) {
+ int i = startingLoc;
+ // if we've got multiple strips, we need to figure out the correct length
+ for (; i < stripIndices.size(); i++) {
+ if (stripIndices.get(i) == -1) {
+ break;
+ }
+ }
+
+ stripLength = i - startingLoc;
+ } else {
+ stripLength = stripIndices.size();
+ }
+
+ primGroupArray[stripCtr].setType(IndexMode.TriangleStrip);
+ primGroupArray[stripCtr].setIndices(new int[stripLength]);
+ primGroupArray[stripCtr].setNumIndices(stripLength);
+
+ int indexCtr = 0;
+ for (int i = startingLoc; i < stripLength + startingLoc; i++) {
+ primGroupArray[stripCtr]._getIndices()[indexCtr++] = stripIndices.get(i);
+ }
+
+ // we add 1 to account for the -1 separating strips
+ // this doesn't break the stitched case since we'll exit the loop
+ startingLoc += stripLength + 1;
+ }
+
+ // next, the list
+ if (tempFaces.size() != 0) {
+ final int faceGroupLoc = numGroups - 1; // the face group is the last one
+ primGroupArray[faceGroupLoc].setType(IndexMode.Triangles);
+ primGroupArray[faceGroupLoc].setIndices(new int[tempFaces.size() * 3]);
+ primGroupArray[faceGroupLoc].setNumIndices(tempFaces.size() * 3);
+ int indexCtr = 0;
+ for (final NvFaceInfo face : tempFaces) {
+ primGroupArray[faceGroupLoc]._getIndices()[indexCtr++] = face._v0;
+ primGroupArray[faceGroupLoc]._getIndices()[indexCtr++] = face._v1;
+ primGroupArray[faceGroupLoc]._getIndices()[indexCtr++] = face._v2;
+ }
+ }
+ }
+
+ // validate generated data against input
+ if (validate) {
+ final int NUMBINS = 100;
+
+ final List<NvFaceInfo> in_bins[] = new List[NUMBINS];
+ for (int i = 0; i < NUMBINS; i++) {
+ in_bins[i] = Lists.newArrayList();
+ }
+
+ // hash input indices on first index
+ for (int i = 0; i < in_indices.length; i += 3) {
+ final NvFaceInfo faceInfo = new NvFaceInfo(in_indices[i], in_indices[i + 1], in_indices[i + 2]);
+ in_bins[in_indices[i] % NUMBINS].add(faceInfo);
+ }
+
+ for (int i = 0; i < numGroups; ++i) {
+ switch (primGroups[i].getType()) {
+ case Triangles: {
+ for (int j = 0; j < primGroups[i].getNumIndices(); j += 3) {
+ final int v0 = primGroups[i]._getIndices()[j];
+ final int v1 = primGroups[i]._getIndices()[j + 1];
+ final int v2 = primGroups[i]._getIndices()[j + 2];
+
+ // ignore degenerates
+ if (NvStripifier.isDegenerate(v0, v1, v2)) {
+ continue;
+ }
+
+ if (!testTriangle(v0, v1, v2, in_bins, NUMBINS)) {
+ throw new IllegalStateException("failed validation");
+ }
+ }
+ break;
+ }
+
+ case TriangleStrip: {
+ boolean flip = false;
+ for (int j = 2; j < primGroups[i].getNumIndices(); ++j) {
+ final int v0 = primGroups[i]._getIndices()[j - 2];
+ int v1 = primGroups[i]._getIndices()[j - 1];
+ int v2 = primGroups[i]._getIndices()[j];
+
+ if (flip) {
+ // swap v1 and v2
+ final int swap = v1;
+ v1 = v2;
+ v2 = swap;
+ }
+
+ // ignore degenerates
+ if (NvStripifier.isDegenerate(v0, v1, v2)) {
+ flip = !flip;
+ continue;
+ }
+
+ if (!testTriangle(v0, v1, v2, in_bins, NUMBINS)) {
+ throw new IllegalStateException("failed validation");
+ }
+
+ flip = !flip;
+ }
+ break;
+ }
+
+ case TriangleFan:
+ default:
+ break;
+ }
+ }
+
+ }
+
+ return primGroups;
+ }
+
+ /**
+ * Function to remap your indices to improve spatial locality in your vertex buffer.
+ *
+ * Note that you must reorder your vertex buffer according to the remapping handed back to you.
+ *
+ * Credit goes to the MS Xbox crew for the idea for this interface.
+ *
+ * @param in_primGroups
+ * array of PrimitiveGroups you want remapped
+ * @param numVerts
+ * number of vertices in your vertex buffer, also can be thought of as the range of acceptable values for
+ * indices in your primitive groups.
+ * @return index remap. old index is key into array, value there is the old location for the vertex. -1 means vertex
+ * was never referenced
+ */
+ public PrimitiveGroup[] remapIndices(final PrimitiveGroup[] in_primGroups,
+ final AtomicReference<int[]> remappedVertices, final int numVerts) {
+ final PrimitiveGroup[] remappedGroups = new PrimitiveGroup[in_primGroups.length];
+
+ // caches oldIndex --> newIndex conversion
+ final int[] indexCache = new int[numVerts];
+ Arrays.fill(indexCache, -1);
+
+ // loop over primitive groups
+ int indexCtr = 0;
+ for (int i = 0; i < in_primGroups.length; i++) {
+ final int numIndices = in_primGroups[i].getNumIndices();
+
+ // init remapped group
+ remappedGroups[i] = new PrimitiveGroup();
+ remappedGroups[i].setType(in_primGroups[i].getType());
+ remappedGroups[i].setNumIndices(numIndices);
+ remappedGroups[i].setIndices(new int[numIndices]);
+
+ for (int j = 0; j < numIndices; j++) {
+ final int cachedIndex = indexCache[in_primGroups[i]._getIndices()[j]];
+ if (cachedIndex == -1) // we haven't seen this index before
+ {
+ // point to "last" vertex in VB
+ remappedGroups[i]._getIndices()[j] = indexCtr;
+
+ // add to index cache, increment
+ indexCache[in_primGroups[i]._getIndices()[j]] = indexCtr++;
+ } else {
+ // we've seen this index before
+ remappedGroups[i]._getIndices()[j] = cachedIndex;
+ }
+ }
+ }
+ if (remappedVertices != null) {
+ remappedVertices.set(indexCache);
+ }
+
+ return remappedGroups;
+ }
+
+ @Override
+ public void visit(final Spatial spatial) {
+ if (spatial instanceof Mesh) {
+ final Mesh mesh = (Mesh) spatial;
+ final MeshData md = mesh.getMeshData();
+ if (md.getTotalPrimitiveCount() < 1 || md.getVertexCount() < 3) {
+ return;
+ }
+ for (final IndexMode mode : md.getIndexModes()) {
+ if (mode != IndexMode.Triangles) {
+ return;
+ }
+ }
+
+ final int[] indices;
+ if (md.getIndices() == null) {
+ indices = new int[md.getVertexCount()];
+ for (int i = 0; i < indices.length; i++) {
+ indices[i] = i;
+ }
+ } else {
+ indices = BufferUtils.getIntArray(md.getIndices());
+ }
+ PrimitiveGroup[] strips = generateStrips(indices, false);
+
+ if (_reorderVertices) {
+ final AtomicReference<int[]> newOrder = new AtomicReference<int[]>();
+ strips = remapIndices(strips, newOrder, md.getVertexCount());
+
+ // ask mesh to apply new vertex order
+ mesh.reorderVertexData(newOrder.get());
+ }
+
+ // construct our new index buffer, modes and counts
+ int indexCount = 0, j = 0, count = 0;
+ for (final PrimitiveGroup group : strips) {
+ if (group.getIndices().length > 0) {
+ count++;
+ }
+ }
+ final int[] counts = new int[count];
+ final IndexMode[] modes = new IndexMode[count];
+ for (final PrimitiveGroup group : strips) {
+ indexCount += group.getIndices().length;
+ if (group.getIndices().length > 0) {
+ modes[j] = group.getType();
+ counts[j++] = group.getIndices().length;
+ }
+ }
+ final IndexBufferData<?> newIndices = BufferUtils.createIndexBufferData(indexCount, md.getVertexCount());
+ for (final PrimitiveGroup group : strips) {
+ final IntBufferData data = new IntBufferData(group.getIndices().length);
+ data.getBuffer().put(group.getIndices());
+ data.rewind();
+ newIndices.put(data);
+ }
+ newIndices.rewind();
+
+ // ask mesh to apply new index data
+ mesh.reorderIndices(newIndices, modes, counts);
+ }
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java
index 01eaff8..4ba92c2 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/PrimitiveGroup.java
@@ -1,62 +1,62 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-import com.ardor3d.renderer.IndexMode;
-
-public class PrimitiveGroup {
-
- private IndexMode _type;
- private int[] _indices;
- private int _numIndices;
-
- PrimitiveGroup() {
- setType(IndexMode.Triangles);
- setIndices(null);
- setNumIndices(0);
- }
-
- int[] _getIndices() {
- return _indices;
- }
-
- public int[] getIndices() {
- if (_indices.length == _numIndices) {
- return _indices;
- }
-
- // crop it down to actual size...
- final int[] realIndices = new int[_numIndices];
- System.arraycopy(_indices, 0, realIndices, 0, _numIndices);
- _indices = realIndices;
- return _indices;
- }
-
- int getNumIndices() {
- return _numIndices;
- }
-
- public IndexMode getType() {
- return _type;
- }
-
- void setType(final IndexMode type) {
- _type = type;
- }
-
- void setIndices(final int[] indices) {
- _indices = indices;
- }
-
- void setNumIndices(final int numIndices) {
- this._numIndices = numIndices;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+import com.ardor3d.renderer.IndexMode;
+
+public class PrimitiveGroup {
+
+ private IndexMode _type;
+ private int[] _indices;
+ private int _numIndices;
+
+ PrimitiveGroup() {
+ setType(IndexMode.Triangles);
+ setIndices(null);
+ setNumIndices(0);
+ }
+
+ int[] _getIndices() {
+ return _indices;
+ }
+
+ public int[] getIndices() {
+ if (_indices.length == _numIndices) {
+ return _indices;
+ }
+
+ // crop it down to actual size...
+ final int[] realIndices = new int[_numIndices];
+ System.arraycopy(_indices, 0, realIndices, 0, _numIndices);
+ _indices = realIndices;
+ return _indices;
+ }
+
+ int getNumIndices() {
+ return _numIndices;
+ }
+
+ public IndexMode getType() {
+ return _type;
+ }
+
+ void setType(final IndexMode type) {
+ _type = type;
+ }
+
+ void setIndices(final int[] indices) {
+ _indices = indices;
+ }
+
+ void setNumIndices(final int numIndices) {
+ this._numIndices = numIndices;
+ }
+}
diff --git a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java
index cf90e5f..ba93840 100644
--- a/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java
+++ b/ardor3d-extras/src/main/java/com/ardor3d/extension/model/util/nvtristrip/VertexCache.java
@@ -1,81 +1,81 @@
-/**
- * Copyright (c) 2008-2012 Ardor Labs, Inc.
- *
- * This file is part of Ardor3D.
- *
- * Ardor3D is free software: you can redistribute it and/or modify it
- * under the terms of its license which may be found in the accompanying
- * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
- */
-
-package com.ardor3d.extension.model.util.nvtristrip;
-
-import java.util.Arrays;
-
-/**
- * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
- */
-final class VertexCache {
-
- private final int[] _entries;
- private final int _numEntries;
-
- public VertexCache(final int size) {
- _numEntries = size;
-
- _entries = new int[_numEntries];
-
- for (int i = 0; i < _numEntries; i++) {
- _entries[i] = -1;
- }
- }
-
- public VertexCache() {
- this(16);
- }
-
- public boolean inCache(final int entry) {
- boolean returnVal = false;
- for (int i = 0; i < _numEntries; i++) {
- if (_entries[i] == entry) {
- returnVal = true;
- break;
- }
- }
-
- return returnVal;
- }
-
- public int addEntry(final int entry) {
- int removed;
-
- removed = _entries[_numEntries - 1];
-
- // push everything right one
- for (int i = _numEntries - 2; i >= 0; i--) {
- _entries[i + 1] = _entries[i];
- }
-
- _entries[0] = entry;
-
- return removed;
- }
-
- public void clear() {
- Arrays.fill(_entries, -1);
- }
-
- public void copy(final VertexCache inVcache) {
- for (int i = 0; i < _numEntries; i++) {
- inVcache.set(i, _entries[i]);
- }
- }
-
- public int at(final int index) {
- return _entries[index];
- }
-
- public void set(final int index, final int value) {
- _entries[index] = value;
- }
-}
+/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.model.util.nvtristrip;
+
+import java.util.Arrays;
+
+/**
+ * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a>
+ */
+final class VertexCache {
+
+ private final int[] _entries;
+ private final int _numEntries;
+
+ public VertexCache(final int size) {
+ _numEntries = size;
+
+ _entries = new int[_numEntries];
+
+ for (int i = 0; i < _numEntries; i++) {
+ _entries[i] = -1;
+ }
+ }
+
+ public VertexCache() {
+ this(16);
+ }
+
+ public boolean inCache(final int entry) {
+ boolean returnVal = false;
+ for (int i = 0; i < _numEntries; i++) {
+ if (_entries[i] == entry) {
+ returnVal = true;
+ break;
+ }
+ }
+
+ return returnVal;
+ }
+
+ public int addEntry(final int entry) {
+ int removed;
+
+ removed = _entries[_numEntries - 1];
+
+ // push everything right one
+ for (int i = _numEntries - 2; i >= 0; i--) {
+ _entries[i + 1] = _entries[i];
+ }
+
+ _entries[0] = entry;
+
+ return removed;
+ }
+
+ public void clear() {
+ Arrays.fill(_entries, -1);
+ }
+
+ public void copy(final VertexCache inVcache) {
+ for (int i = 0; i < _numEntries; i++) {
+ inVcache.set(i, _entries[i]);
+ }
+ }
+
+ public int at(final int index) {
+ return _entries[index];
+ }
+
+ public void set(final int index, final int value) {
+ _entries[index] = value;
+ }
+}