From 28a64bbfcf07d3ca8aca72960cddad77e086c104 Mon Sep 17 00:00:00 2001 From: "bre..ns" Date: Fri, 6 Jan 2012 01:21:04 +0000 Subject: [PATCH] added first cut of navmesh generator plugin to SDK git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8982 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- sdk/jme3-navmesh-gen/build.xml | 8 + sdk/jme3-navmesh-gen/lib/cai-nmgen-0.2.0.jar | Bin 0 -> 47904 bytes sdk/jme3-navmesh-gen/manifest.mf | 7 + sdk/jme3-navmesh-gen/nbproject/build-impl.xml | 45 ++ .../nbproject/genfiles.properties | 8 + .../nbproject/project.properties | 6 + sdk/jme3-navmesh-gen/nbproject/project.xml | 134 ++++ .../nbproject/suite.properties | 1 + .../src/com/jme3/gde/nmgen/Bundle.properties | 7 + .../src/com/jme3/gde/nmgen/NavMeshAction.java | 67 ++ .../gde/nmgen/NavMeshCameraController.java | 45 ++ .../com/jme3/gde/nmgen/NavMeshController.java | 199 ++++++ .../com/jme3/gde/nmgen/NavMeshGenerator.java | 344 +++++++++ .../jme3/gde/nmgen/NavMeshToolController.java | 63 ++ .../jme3/gde/nmgen/NavMeshTopComponent.form | 379 ++++++++++ .../jme3/gde/nmgen/NavMeshTopComponent.java | 674 ++++++++++++++++++ .../gde/nmgen/NavMeshTopComponentSettings.xml | 12 + .../gde/nmgen/NavMeshTopComponentWstcref.xml | 11 + .../src/com/jme3/gde/nmgen/layer.xml | 45 ++ .../src/com/jme3/gde/nmgen/logo.png | Bin 0 -> 3403 bytes .../jme3/gde/templates/BasicGameProject.zip | Bin 5695 -> 5695 bytes sdk/jme3-terrain-editor/manifest.mf | 14 +- sdk/nbproject/project.properties | 162 ++--- 23 files changed, 2144 insertions(+), 87 deletions(-) create mode 100644 sdk/jme3-navmesh-gen/build.xml create mode 100644 sdk/jme3-navmesh-gen/lib/cai-nmgen-0.2.0.jar create mode 100644 sdk/jme3-navmesh-gen/manifest.mf create mode 100644 sdk/jme3-navmesh-gen/nbproject/build-impl.xml create mode 100644 sdk/jme3-navmesh-gen/nbproject/genfiles.properties create mode 100644 sdk/jme3-navmesh-gen/nbproject/project.properties create mode 100644 sdk/jme3-navmesh-gen/nbproject/project.xml create mode 100644 sdk/jme3-navmesh-gen/nbproject/suite.properties create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/Bundle.properties create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshAction.java create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshCameraController.java create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshController.java create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshGenerator.java create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshToolController.java create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.form create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.java create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponentSettings.xml create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponentWstcref.xml create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/layer.xml create mode 100644 sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/logo.png diff --git a/sdk/jme3-navmesh-gen/build.xml b/sdk/jme3-navmesh-gen/build.xml new file mode 100644 index 000000000..230f0c291 --- /dev/null +++ b/sdk/jme3-navmesh-gen/build.xml @@ -0,0 +1,8 @@ + + + + + + Builds, tests, and runs the project com.jme3.gde.nmgen. + + diff --git a/sdk/jme3-navmesh-gen/lib/cai-nmgen-0.2.0.jar b/sdk/jme3-navmesh-gen/lib/cai-nmgen-0.2.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..a95c811238aecf80eaa2926f0adb7f102b5dbb28 GIT binary patch literal 47904 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\?_Jv==;K(bTy3PjC04>3axD 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 + + + + + + + + + + + + You must set 'suite.dir' to point to your containing module suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/jme3-navmesh-gen/nbproject/genfiles.properties b/sdk/jme3-navmesh-gen/nbproject/genfiles.properties new file mode 100644 index 000000000..b87513df8 --- /dev/null +++ b/sdk/jme3-navmesh-gen/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=0efc30b9 +build.xml.script.CRC32=d1f1ecca +build.xml.stylesheet.CRC32=a56c6a5b@1.46.2 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=0efc30b9 +nbproject/build-impl.xml.script.CRC32=fe180a9d +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.46.2 diff --git a/sdk/jme3-navmesh-gen/nbproject/project.properties b/sdk/jme3-navmesh-gen/nbproject/project.properties new file mode 100644 index 000000000..9d2630806 --- /dev/null +++ b/sdk/jme3-navmesh-gen/nbproject/project.properties @@ -0,0 +1,6 @@ +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +license.file=../license-jme.txt +nbm.homepage=http://www.jmonkeyengine.com +nbm.module.author=Brent Owens +spec.version.base=3.0.0 diff --git a/sdk/jme3-navmesh-gen/nbproject/project.xml b/sdk/jme3-navmesh-gen/nbproject/project.xml new file mode 100644 index 000000000..644681add --- /dev/null +++ b/sdk/jme3-navmesh-gen/nbproject/project.xml @@ -0,0 +1,134 @@ + + + org.netbeans.modules.apisupport.project + + + com.jme3.gde.nmgen + + + + com.jme3.gde.core + + + + 1 + 3.0.0 + + + + com.jme3.gde.core.baselibs + + + + 1 + 3.0.0 + + + + org.jdesktop.beansbinding + + + + 1 + 1.11.0.121 + + + + org.netbeans.api.progress + + + + 1 + 1.24 + + + + org.netbeans.modules.settings + + + + 1 + 1.31 + + + + org.openide.awt + + + + 7.31 + + + + org.openide.dialogs + + + + 7.20 + + + + org.openide.explorer + + + + 6.35 + + + + org.openide.filesystems + + + + 7.47 + + + + org.openide.loaders + + + + 7.21 + + + + org.openide.nodes + + + + 7.21 + + + + org.openide.util + + + + 8.15 + + + + org.openide.util.lookup + + + + 8.8 + + + + org.openide.windows + + + + 6.40 + + + + + + ext/cai-nmgen-0.2.0.jar + release/modules/ext/cai-nmgen-0.2.0.jar + + + + diff --git a/sdk/jme3-navmesh-gen/nbproject/suite.properties b/sdk/jme3-navmesh-gen/nbproject/suite.properties new file mode 100644 index 000000000..364e160e1 --- /dev/null +++ b/sdk/jme3-navmesh-gen/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/Bundle.properties b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/Bundle.properties new file mode 100644 index 000000000..07b29c5d5 --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/Bundle.properties @@ -0,0 +1,7 @@ +OpenIDE-Module-Long-Description=\ + Nav Mesh Generator - generates a triangle mesh to be used for navigation +OpenIDE-Module-Name=NavMeshGen +OpenIDE-Module-Display-Category=Nav Mesh Generator +CTL_NavMeshTopComponent=Nav Mesh Editor +HINT_NavMeshTopComponent=Nav Mesh generator window +OpenIDE-Module-Short-Description=NavMeshGen diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshAction.java b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshAction.java new file mode 100644 index 000000000..06eb6542a --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshAction.java @@ -0,0 +1,67 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.gde.nmgen; + +import com.jme3.asset.DesktopAssetManager; +import com.jme3.gde.core.assets.BinaryModelDataObject; +import com.jme3.gde.core.assets.ProjectAssetManager; +import com.jme3.scene.Spatial; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.NotifyDescriptor.Confirmation; + +/** + * + * @author sploreg + */ +public class NavMeshAction implements ActionListener { + + private final BinaryModelDataObject context; + + public NavMeshAction(BinaryModelDataObject context) { + this.context = context; + } + + public void actionPerformed(ActionEvent ev) { + final ProjectAssetManager manager = context.getLookup().lookup(ProjectAssetManager.class); + if (manager == null) { + return; + } + Runnable call = new Runnable() { + + public void run() { + ProgressHandle progressHandle = ProgressHandleFactory.createHandle("Opening in Nav Mesh Editor"); + progressHandle.start(); + + + final Spatial asset = context.loadAsset(); + + if(asset!=null){ + java.awt.EventQueue.invokeLater(new Runnable() { + + public void run() { + ((DesktopAssetManager)manager.getManager()).clearCache(); + NavMeshTopComponent composer = NavMeshTopComponent.findInstance(); + composer.openScene(asset, context, manager); + } + }); + }else { + Confirmation msg = new NotifyDescriptor.Confirmation( + "Error opening " + context.getPrimaryFile().getNameExt(), + NotifyDescriptor.OK_CANCEL_OPTION, + NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notify(msg); + } + progressHandle.finish(); + } + }; + new Thread(call).start(); + } + +} diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshCameraController.java b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshCameraController.java new file mode 100644 index 000000000..7241385df --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshCameraController.java @@ -0,0 +1,45 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.gde.nmgen; + +import com.jme3.app.Application; +import com.jme3.gde.core.scene.SceneApplication; +import com.jme3.gde.core.scene.controller.AbstractCameraController; +import com.jme3.renderer.Camera; + +/** + * + * @author sploreg + */ +public class NavMeshCameraController extends AbstractCameraController { + + private NavMeshToolController toolController; + private NavMeshController editorController; + private Application app; + + public NavMeshCameraController(Camera cam) { + super(cam, SceneApplication.getApplication().getInputManager()); + app = SceneApplication.getApplication(); + } + + public void setEditorController(NavMeshController editorController) { + this.editorController = editorController; + } + + public void setToolController(NavMeshToolController toolController) { + this.toolController = toolController; + } + + @Override + public boolean useCameraControls() { + return true; + } + + @Override + protected void checkClick(int button, boolean pressed) { + //TODO + } + +} diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshController.java b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshController.java new file mode 100644 index 000000000..ee7b20b66 --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshController.java @@ -0,0 +1,199 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.gde.nmgen; + +import com.jme3.bounding.BoundingBox; +import com.jme3.gde.core.assets.AssetDataObject; +import com.jme3.gde.core.scene.SceneApplication; +import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.Spatial.CullHint; +import com.jme3.terrain.Terrain; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import jme3tools.optimize.GeometryBatchFactory; +import org.critterai.nmgen.IntermediateData; + +/** + * + * @author sploreg + */ +public class NavMeshController { + private JmeSpatial jmeRootNode; + private Node rootNode; + private AssetDataObject currentFileObject; + private NavMeshTopComponent topComponent; + //private NavMesh navMesh; // current nav mesh in this scene + private Material navMaterial; + + public NavMeshController(JmeSpatial jmeRootNode, AssetDataObject currentFileObject, NavMeshTopComponent topComponent) { + this.jmeRootNode = jmeRootNode; + this.currentFileObject = currentFileObject; + this.topComponent = topComponent; + rootNode = this.jmeRootNode.getLookup().lookup(Node.class); + } + + protected void cleanup() { + + } + + public void setNeedsSave(boolean state) { + currentFileObject.setModified(state); + } + + protected Mesh generateNavMesh(float cellSize, + float cellHeight, + float minTraversableHeight, + float maxTraversableStep, + float maxTraversableSlope, + boolean clipLedges, + float traversableAreaBorderSize, + float smoothingThreshold, + boolean useConservativeExpansion, + float minUnconnectedRegionSize, + float mergeRegionSize, + float maxEdgeLength, + float edgeMaxDeviation, + float maxVertsPerPoly, + float contourSampleDistance, + float contourMaxDeviation) + { + NavMeshGenerator generator = new NavMeshGenerator(); + generator.setCellSize(cellSize); + generator.setCellHeight(cellHeight); + generator.setMinTraversableHeight(minTraversableHeight); + generator.setMaxTraversableStep(maxTraversableStep); + generator.setMaxTraversableSlope(maxTraversableSlope); + generator.setClipLedges(clipLedges); + generator.setTraversableAreaBorderSize(traversableAreaBorderSize); + generator.setSmoothingThreshold((int)smoothingThreshold); + generator.setUseConservativeExpansion(useConservativeExpansion); + generator.setMergeRegionSize((int)mergeRegionSize); + generator.setMaxEdgeLength(maxEdgeLength); + generator.setEdgeMaxDeviation(edgeMaxDeviation); + generator.setMaxVertsPerPoly((int)maxVertsPerPoly); + generator.setContourSampleDistance(contourSampleDistance); + generator.setContourMaxDeviation(contourMaxDeviation); + + IntermediateData id = new IntermediateData(); + + generator.setIntermediateData(null); + + Mesh mesh = new Mesh(); + //NavMesh navMesh = new NavMesh(); + + GeometryBatchFactory.mergeGeometries(findGeometries(rootNode, new LinkedList(), generator), mesh); + Mesh optiMesh = generator.optimize(mesh); + + final Geometry navMesh = new Geometry("NavMesh"); + navMesh.setMesh(optiMesh); + navMesh.setCullHint(CullHint.Always); + navMesh.setModelBound(new BoundingBox()); + + Spatial previous = rootNode.getChild("NavMesh"); + if (previous != null) + previous.removeFromParent(); + + SceneApplication.getApplication().enqueue(new Callable() { + public Void call() throws Exception { + rootNode.attachChild(navMesh); + return null; + } + }); + + jmeRootNode.refresh(true); + + //navMesh.setUserData("NavMeshGenerator", generator); + + //navMesh.loadFromMesh(optiMesh); + //saveNavMesh(navMesh, generator); + + setNeedsSave(true); + + return optiMesh; + } + + /** + * NavMesh saves as user data on the scene's root node + */ + /*private void saveNavMesh(NavMesh navMesh, NavMeshGenerator generator) { + rootNode.setUserData("NavMesh", navMesh); + rootNode.setUserData("NavMeshGenerator", generator); + this.navMesh = navMesh; + } + + protected NavMesh getNavMesh() { + if (navMesh == null) { + navMesh = findNavMesh(); + } + return navMesh; + } + + protected NavMesh findNavMesh() { + return rootNode.getUserData("NavMesh"); + }*/ + + protected NavMeshGenerator getNavMeshGenerator() { + return rootNode.getUserData("NavMeshGenerator"); + } + + private List findGeometries(Node node, List geoms, NavMeshGenerator generator) { + for (Iterator it = node.getChildren().iterator(); it.hasNext();) { + Spatial spatial = it.next(); + if (spatial instanceof Geometry) { + geoms.add((Geometry) spatial); + } else if (spatial instanceof Node) { + if (spatial instanceof Terrain) { + Mesh merged = generator.terrain2mesh((Terrain)spatial); + Geometry g = new Geometry("mergedTerrain"); + g.setMesh(merged); + geoms.add(g); + } else + findGeometries((Node) spatial, geoms, generator); + } + } + return geoms; + } + + /** + * Create polygons from each of the Nav Mesh Cells. + * @return a single mesh representing the nav mesh + */ + /*protected Mesh createDebugMesh() { + NavMesh navMesh = getNavMesh(); + if (navMesh != null) { + List meshes = new ArrayList(); + for (int i=0; i 0. + */ + public float getCellHeight() { + return cellHeight; + } + + /** + * @param cellHeight - The height resolution used when sampling the source mesh. Value must be > 0. + */ + public void setCellHeight(float cellHeight) { + this.cellHeight = cellHeight; + } + + /** + * @return The width and depth resolution used when sampling the the source mesh. + */ + public float getCellSize() { + return cellSize; + } + + /** + * @param cellSize - The width and depth resolution used when sampling the the source mesh. + */ + public void setCellSize(float cellSize) { + this.cellSize = cellSize; + } + + public boolean isClipLedges() { + return clipLedges; + } + + public void setClipLedges(boolean clipLedges) { + this.clipLedges = clipLedges; + } + + public float getContourMaxDeviation() { + return contourMaxDeviation; + } + + public void setContourMaxDeviation(float contourMaxDeviation) { + this.contourMaxDeviation = contourMaxDeviation; + } + + public float getContourSampleDistance() { + return contourSampleDistance; + } + + public void setContourSampleDistance(float contourSampleDistance) { + this.contourSampleDistance = contourSampleDistance; + } + + public float getEdgeMaxDeviation() { + return edgeMaxDeviation; + } + + public void setEdgeMaxDeviation(float edgeMaxDeviation) { + this.edgeMaxDeviation = edgeMaxDeviation; + } + + public float getMaxEdgeLength() { + return maxEdgeLength; + } + + public void setMaxEdgeLength(float maxEdgeLength) { + this.maxEdgeLength = maxEdgeLength; + } + + public float getMaxTraversableSlope() { + return maxTraversableSlope; + } + + public void setMaxTraversableSlope(float maxTraversableSlope) { + this.maxTraversableSlope = maxTraversableSlope; + } + + public float getMaxTraversableStep() { + return maxTraversableStep; + } + + public void setMaxTraversableStep(float maxTraversableStep) { + this.maxTraversableStep = maxTraversableStep; + } + + public int getMaxVertsPerPoly() { + return maxVertsPerPoly; + } + + public void setMaxVertsPerPoly(int maxVertsPerPoly) { + this.maxVertsPerPoly = maxVertsPerPoly; + } + + public int getMergeRegionSize() { + return mergeRegionSize; + } + + public void setMergeRegionSize(int mergeRegionSize) { + this.mergeRegionSize = mergeRegionSize; + } + + public float getMinTraversableHeight() { + return minTraversableHeight; + } + + public void setMinTraversableHeight(float minTraversableHeight) { + this.minTraversableHeight = minTraversableHeight; + } + + public int getMinUnconnectedRegionSize() { + return minUnconnectedRegionSize; + } + + public void setMinUnconnectedRegionSize(int minUnconnectedRegionSize) { + this.minUnconnectedRegionSize = minUnconnectedRegionSize; + } + + public NavmeshGenerator getNmgen() { + return nmgen; + } + + public void setNmgen(NavmeshGenerator nmgen) { + this.nmgen = nmgen; + } + + public int getSmoothingThreshold() { + return smoothingThreshold; + } + + public void setSmoothingThreshold(int smoothingThreshold) { + this.smoothingThreshold = smoothingThreshold; + } + + public float getTraversableAreaBorderSize() { + return traversableAreaBorderSize; + } + + public void setTraversableAreaBorderSize(float traversableAreaBorderSize) { + this.traversableAreaBorderSize = traversableAreaBorderSize; + } + + public boolean isUseConservativeExpansion() { + return useConservativeExpansion; + } + + public void setUseConservativeExpansion(boolean useConservativeExpansion) { + this.useConservativeExpansion = useConservativeExpansion; + } + + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(cellSize, "cellSize", 1f); + oc.write(cellHeight, "cellHeight", 1.5f); + oc.write(minTraversableHeight, "minTraversableHeight", 7.5f); + oc.write(maxTraversableStep, "maxTraversableStep", 1f); + oc.write(maxTraversableSlope, "maxTraversableSlope", 48f); + oc.write(clipLedges, "clipLedges", false); + oc.write(traversableAreaBorderSize, "traversableAreaBorderSize", 1.2f); + oc.write(smoothingThreshold, "smoothingThreshold", 2); + oc.write(useConservativeExpansion, "useConservativeExpansion", true); + oc.write(minUnconnectedRegionSize, "minUnconnectedRegionSize", 3); + oc.write(mergeRegionSize, "mergeRegionSize", 10); + oc.write(maxEdgeLength, "maxEdgeLength", 0); + oc.write(edgeMaxDeviation, "edgeMaxDeviation", 2.4f); + oc.write(maxVertsPerPoly, "maxVertsPerPoly", 6); + oc.write(contourSampleDistance, "contourSampleDistance", 25); + oc.write(contourMaxDeviation, "contourMaxDeviation", 25); + } + + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + cellSize = ic.readFloat("cellSize", 1f); + cellHeight = ic.readFloat("cellHeight", 1.5f); + minTraversableHeight = ic.readFloat("minTraversableHeight", 7.5f); + maxTraversableStep = ic.readFloat("maxTraversableStep", 1f); + maxTraversableSlope = ic.readFloat("maxTraversableSlope", 48f); + clipLedges = ic.readBoolean("clipLedges", false); + traversableAreaBorderSize = ic.readFloat("traversableAreaBorderSize", 1.2f); + smoothingThreshold = (int)ic.readFloat("smoothingThreshold", 2); + useConservativeExpansion = ic.readBoolean("useConservativeExpansion", true); + minUnconnectedRegionSize = (int)ic.readFloat("minUnconnectedRegionSize", 3); + mergeRegionSize = (int)ic.readFloat("mergeRegionSize", 10); + maxEdgeLength = ic.readFloat("maxEdgeLength", 0); + edgeMaxDeviation = ic.readFloat("edgeMaxDeviation", 2.4f); + maxVertsPerPoly = (int)ic.readFloat("maxVertsPerPoly", 6); + contourSampleDistance = ic.readFloat("contourSampleDistance", 25); + contourMaxDeviation = ic.readFloat("contourMaxDeviation", 25); + } +} diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshToolController.java b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshToolController.java new file mode 100644 index 000000000..9a081f9fb --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshToolController.java @@ -0,0 +1,63 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.gde.nmgen; + +import com.jme3.asset.AssetManager; +import com.jme3.gde.core.scene.controller.SceneToolController; +import com.jme3.gde.core.sceneexplorer.nodes.JmeNode; +import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; + +/** + * + * @author sploreg + */ +public class NavMeshToolController extends SceneToolController { + + private NavMeshController editorController; + private NavMeshCameraController cameraController; + private JmeSpatial jmeRootNode; + private Geometry navGeom; + private Material navMaterial; + + public NavMeshToolController(Node toolsNode, AssetManager manager, JmeNode rootNode) { + super(toolsNode, manager); + this.jmeRootNode = rootNode; + } + + public void setEditorController(NavMeshController editorController) { + this.editorController = editorController; + } + + public void setCameraController(NavMeshCameraController cameraController) { + this.cameraController = cameraController; + } + + private Material getNavMaterial() { + if (navMaterial != null) + return navMaterial; + navMaterial = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + navMaterial.setColor("Color", ColorRGBA.Green); + navMaterial.getAdditionalRenderState().setWireframe(true); + return navMaterial; + } + + /** + * Render the new nav mesh + */ + protected void attachNavMesh(Mesh navMesh) { + if (navMesh == null) + return; + if (navGeom == null) + navGeom = new Geometry("NavMesh"); + navGeom.setMesh(navMesh); + navGeom.setMaterial(getNavMaterial()); + toolsNode.attachChild(navGeom); + } +} diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.form b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.form new file mode 100644 index 000000000..cd7143650 --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.form @@ -0,0 +1,379 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.java b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.java new file mode 100644 index 000000000..cb537f9d7 --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponent.java @@ -0,0 +1,674 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.jme3.gde.nmgen; + + + +import com.jme3.gde.core.assets.AssetDataObject; +import com.jme3.gde.core.assets.ProjectAssetManager; +import com.jme3.gde.core.scene.PreviewRequest; +import com.jme3.gde.core.scene.SceneApplication; +import com.jme3.gde.core.scene.SceneListener; +import com.jme3.gde.core.scene.SceneRequest; +import com.jme3.gde.core.sceneexplorer.nodes.JmeNode; +import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial; +import com.jme3.gde.core.sceneexplorer.nodes.NodeUtility; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import java.util.Collection; +import java.util.logging.Logger; +import org.openide.util.LookupEvent; +import org.openide.util.NbBundle; +import org.openide.windows.TopComponent; +import org.netbeans.api.settings.ConvertAsProperties; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup.Result; +import org.openide.util.LookupListener; +import org.openide.util.Utilities; +import org.openide.windows.WindowManager; + + + +/** + * Top component which displays the Nav Mesh editor. + * + * http://critterai.org/javadoc/nmgen/org/critterai/nmgen/NavmeshGenerator.html + */ +@ConvertAsProperties( + dtd="-//com.sploreg.tritium.editor.navmesh//NavMesh//EN", + autostore=false +) +public final class NavMeshTopComponent extends TopComponent implements SceneListener, LookupListener { + + private static NavMeshTopComponent instance; + static final String ICON_PATH = "com/sploreg/tritium/editor/navmesh/logo.png"; + private static final String PREFERRED_ID = "NavMeshTopComponent"; + private SceneRequest currentRequest; + private final Result result; + private NavMeshController editorController; + private NavMeshToolController toolController; + private NavMeshCameraController cameraController; + private JmeSpatial selectedSpat; + + public NavMeshTopComponent() { + initComponents(); + setName(NbBundle.getMessage(NavMeshTopComponent.class, "CTL_NavMeshTopComponent")); + setToolTipText(NbBundle.getMessage(NavMeshTopComponent.class, "HINT_NavMeshTopComponent")); + setIcon(ImageUtilities.loadImage(ICON_PATH, true)); + result = Utilities.actionsGlobalContext().lookupResult(JmeSpatial.class); + } + + /** + * Gets default instance. Do not use directly: reserved for *.settings files only, + * i.e. deserialization routines; otherwise you could get a non-deserialized instance. + * To obtain the singleton instance, use {@link #findInstance}. + */ + public static synchronized NavMeshTopComponent getDefault() { + if (instance == null) { + instance = new NavMeshTopComponent(); + } + return instance; + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + cellSizeField = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + cellHeightField = new javax.swing.JTextField(); + jLabel3 = new javax.swing.JLabel(); + minTraversableHeightField = new javax.swing.JTextField(); + jLabel4 = new javax.swing.JLabel(); + maxTraversableStepField = new javax.swing.JTextField(); + jLabel5 = new javax.swing.JLabel(); + maxTraversableSlopeField = new javax.swing.JTextField(); + jLabel6 = new javax.swing.JLabel(); + clipLedgesBox = new javax.swing.JCheckBox(); + jLabel7 = new javax.swing.JLabel(); + traversableAreaBorderSizeField = new javax.swing.JTextField(); + jLabel8 = new javax.swing.JLabel(); + smoothingThresholdField = new javax.swing.JTextField(); + jLabel9 = new javax.swing.JLabel(); + useConservativeExpansionBox = new javax.swing.JCheckBox(); + jLabel10 = new javax.swing.JLabel(); + mergeRegionSizeField = new javax.swing.JTextField(); + jLabel11 = new javax.swing.JLabel(); + maxEdgeLengthField = new javax.swing.JTextField(); + jLabel12 = new javax.swing.JLabel(); + edgeMaxDeviationField = new javax.swing.JTextField(); + jLabel13 = new javax.swing.JLabel(); + maxVertsPerPolyField = new javax.swing.JTextField(); + jLabel14 = new javax.swing.JLabel(); + contourSampleDistanceField = new javax.swing.JTextField(); + jLabel15 = new javax.swing.JLabel(); + contourMaxDeviationField = new javax.swing.JTextField(); + jLabel16 = new javax.swing.JLabel(); + minUnconnectedRegionSizeField = new javax.swing.JTextField(); + jToolBar1 = new javax.swing.JToolBar(); + jButton1 = new javax.swing.JButton(); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, "Cell Size"); + jLabel1.setToolTipText("The width and depth resolution used when sampling the source mesh"); + + cellSizeField.setText("1"); + cellSizeField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cellSizeFieldActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, "Cell Height"); + jLabel2.setToolTipText("The height resolution used when sampling the source mesh"); + + cellHeightField.setText("1.5"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, "Min Traversable Height"); + jLabel3.setToolTipText("Represents the minimum floor to ceiling height that will still allow the floor area to be considered walkable"); + + minTraversableHeightField.setText("7.5"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel4, "Max Traversable Step"); + jLabel4.setToolTipText("Represents the maximum ledge height that is considered to still be walkable"); + + maxTraversableStepField.setText("1"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel5, "Max Traversable Slope"); + jLabel5.setToolTipText("The maximum slope that is considered walkable. (Degrees)"); + + maxTraversableSlopeField.setText("48"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel6, "Clip Ledges"); + jLabel6.setToolTipText("Indicates whether ledges should be marked as unwalkable"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel7, "Traversable Area Border Size"); + jLabel7.setToolTipText("Represents the closest any part of the navmesh can get to an obstruction in the source mesh"); + + traversableAreaBorderSizeField.setText("1.2"); + traversableAreaBorderSizeField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + traversableAreaBorderSizeFieldActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel8, "Smoothing Threshold"); + jLabel8.setToolTipText("The amount of smoothing to be performed when generating the distance field"); + + smoothingThresholdField.setText("2"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel9, "Use Conservative Expansion"); + jLabel9.setToolTipText("Applies extra algorithms to regions to help prevent poorly formed regions from forming. Enabling this feature significantly increased processing cost."); + + useConservativeExpansionBox.setSelected(true); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel10, "Merge Region Size"); + jLabel10.setToolTipText("Any regions smaller than this size will, if possible, be merged with larger regions. (Voxels)"); + + mergeRegionSizeField.setText("10"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel11, "Max Edge Length"); + jLabel11.setToolTipText("The maximum length of polygon edges that represent the border of the navmesh"); + + maxEdgeLengthField.setText("0"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel12, "Edge Max Deviation"); + jLabel12.setToolTipText("The maximum distance the edge of the navmesh may deviate from the source geometry. Setting this lower will result in the navmesh edges following the geometry contour more accurately at the expense of an increased triangle count."); + + edgeMaxDeviationField.setText("2.4"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel13, "Max Verts Per Poly"); + jLabel13.setToolTipText("The maximum number of vertices per polygon for polygons generated during the voxel to polygon conversion stage. Higher values reduce performance, but can also result in better formed triangles in the navmesh. A value of around 6 is generally adequate with diminishing returns for values higher than 6."); + + maxVertsPerPolyField.setText("6"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel14, "Contour Sample Distance"); + jLabel14.setToolTipText("Sets the sampling distance to use when matching the navmesh to the surface of the original geometry. Impacts how well the final mesh conforms to the original geometry's surface contour. Higher values result in a navmesh which conforms more closely to the original geometry's surface at the cost of a higher final triangle count and higher processing cost"); + + contourSampleDistanceField.setText("25"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel15, "Contour Max Deviation"); + jLabel15.setToolTipText("The maximum distance the surface of the navmesh may deviate from the surface of the original geometry."); + + contourMaxDeviationField.setText("25"); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel16, "Min Unconnected Region Size"); + jLabel16.setToolTipText("The minimum region size for unconnected (island) regions. (Voxels) "); + + minUnconnectedRegionSizeField.setText("3"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jLabel16) + .addComponent(jLabel15) + .addComponent(jLabel14) + .addComponent(jLabel13) + .addComponent(jLabel12) + .addComponent(jLabel11) + .addComponent(jLabel10) + .addComponent(jLabel9) + .addComponent(jLabel8) + .addComponent(jLabel7) + .addComponent(jLabel6) + .addComponent(jLabel5) + .addComponent(jLabel4) + .addComponent(jLabel3) + .addComponent(jLabel2) + .addComponent(jLabel1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(minUnconnectedRegionSizeField) + .addComponent(contourMaxDeviationField) + .addComponent(contourSampleDistanceField) + .addComponent(maxVertsPerPolyField) + .addComponent(edgeMaxDeviationField) + .addComponent(maxEdgeLengthField) + .addComponent(smoothingThresholdField) + .addComponent(maxTraversableSlopeField) + .addComponent(maxTraversableStepField) + .addComponent(minTraversableHeightField) + .addComponent(cellSizeField, javax.swing.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE) + .addComponent(cellHeightField) + .addComponent(clipLedgesBox) + .addComponent(traversableAreaBorderSizeField) + .addComponent(useConservativeExpansionBox) + .addComponent(mergeRegionSizeField)) + .addContainerGap(594, Short.MAX_VALUE)) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(31, 31, 31) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cellSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent(cellHeightField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(minTraversableHeightField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel4) + .addComponent(maxTraversableStepField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel5) + .addComponent(maxTraversableSlopeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel6) + .addComponent(clipLedgesBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel7) + .addComponent(traversableAreaBorderSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel8) + .addComponent(smoothingThresholdField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel9) + .addComponent(useConservativeExpansionBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel10) + .addComponent(mergeRegionSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel11) + .addComponent(maxEdgeLengthField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel12) + .addComponent(edgeMaxDeviationField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel13) + .addComponent(maxVertsPerPolyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel14) + .addComponent(contourSampleDistanceField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel15) + .addComponent(contourMaxDeviationField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel16) + .addComponent(minUnconnectedRegionSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(57, Short.MAX_VALUE)) + ); + + jScrollPane1.setViewportView(jPanel1); + + jToolBar1.setRollover(true); + + org.openide.awt.Mnemonics.setLocalizedText(jButton1, "Generate"); + jButton1.setFocusable(false); + jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + jToolBar1.add(jButton1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jToolBar1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 664, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 664, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 194, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + +private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + generateNavMesh(); +}//GEN-LAST:event_jButton1ActionPerformed + +private void cellSizeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cellSizeFieldActionPerformed +// TODO add your handling code here: +}//GEN-LAST:event_cellSizeFieldActionPerformed + +private void traversableAreaBorderSizeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_traversableAreaBorderSizeFieldActionPerformed +// TODO add your handling code here: +}//GEN-LAST:event_traversableAreaBorderSizeFieldActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField cellHeightField; + private javax.swing.JTextField cellSizeField; + private javax.swing.JCheckBox clipLedgesBox; + private javax.swing.JTextField contourMaxDeviationField; + private javax.swing.JTextField contourSampleDistanceField; + private javax.swing.JTextField edgeMaxDeviationField; + private javax.swing.JButton jButton1; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel10; + private javax.swing.JLabel jLabel11; + private javax.swing.JLabel jLabel12; + private javax.swing.JLabel jLabel13; + private javax.swing.JLabel jLabel14; + private javax.swing.JLabel jLabel15; + private javax.swing.JLabel jLabel16; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JLabel jLabel5; + private javax.swing.JLabel jLabel6; + private javax.swing.JLabel jLabel7; + private javax.swing.JLabel jLabel8; + private javax.swing.JLabel jLabel9; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JToolBar jToolBar1; + private javax.swing.JTextField maxEdgeLengthField; + private javax.swing.JTextField maxTraversableSlopeField; + private javax.swing.JTextField maxTraversableStepField; + private javax.swing.JTextField maxVertsPerPolyField; + private javax.swing.JTextField mergeRegionSizeField; + private javax.swing.JTextField minTraversableHeightField; + private javax.swing.JTextField minUnconnectedRegionSizeField; + private javax.swing.JTextField smoothingThresholdField; + private javax.swing.JTextField traversableAreaBorderSizeField; + private javax.swing.JCheckBox useConservativeExpansionBox; + // End of variables declaration//GEN-END:variables + + @Override + public void componentOpened() { + super.componentOpened(); + if (currentRequest == null) { + close(); + } + } + + @Override + public void componentClosed() { + super.componentClosed(); + if (currentRequest != null) { + SceneApplication.getApplication().closeScene(currentRequest); + } + } + + void writeProperties(java.util.Properties p) { + // better to version settings since initial version as advocated at + // http://wiki.apidesign.org/wiki/PropertyFiles + p.setProperty("version", "1.0"); + // TODO store your settings + } + void readProperties(java.util.Properties p) { + String version = p.getProperty("version"); + // TODO read your settings according to their version + } + + /** + * Obtain the TerrainEditorTopComponent instance. Never call {@link #getDefault} directly! + */ + public static synchronized NavMeshTopComponent findInstance() { + TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID); + if (win == null) { + Logger.getLogger(NavMeshTopComponent.class.getName()).warning( + "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); + return getDefault(); + } + if (win instanceof NavMeshTopComponent) { + return (NavMeshTopComponent) win; + } + Logger.getLogger(NavMeshTopComponent.class.getName()).warning( + "There seem to be multiple components with the '" + PREFERRED_ID + + "' ID. That is a potential source of errors and unexpected behavior."); + return getDefault(); + } + + // run on swing thread + public void openScene(Spatial spat, AssetDataObject file, ProjectAssetManager manager) { + cleanupControllers(); + SceneApplication.getApplication().addSceneListener(this); + result.addLookupListener(this); + //TODO: handle request change + Node node; + if (spat instanceof Node) { + node = (Node) spat; + } else { + node = new Node(); + node.attachChild(spat); + } + JmeNode jmeNode = NodeUtility.createNode(node, file, false); + SceneRequest request = new SceneRequest(this, jmeNode, manager); + request.setDataObject(file); + + addSaveNode(jmeNode); + + Logger.getLogger(NavMeshTopComponent.class.getName()).finer("NavMesh openScene " + file.getName()); + + if (editorController != null) { + editorController.cleanup(); + } + editorController = new NavMeshController(jmeNode, file, this); + this.currentRequest = request; + request.setWindowTitle("NavMesh - " + manager.getRelativeAssetPath(file.getPrimaryFile().getPath())); + request.setToolNode(new Node("NavMeshEditorToolNode")); + SceneApplication.getApplication().openScene(request); + } + + @Override + public void previewCreated(PreviewRequest request) { + } + + // run on GL thread + /*@Override + public void sceneRequested(SceneRequest request) { + if (request.equals(currentRequest)) { + Logger.getLogger(NavMeshTopComponent.class.getName()).finer("Terrain sceneRequested " + request.getWindowTitle()); + + setSceneInfo(currentRequest.getJmeNode(), true); + + if (cameraController != null) { + cameraController.disable(); + } + if (toolController != null) { + toolController.cleanup(); + } + + //for (int i=0; i items = (Collection) result.allInstances(); + for (JmeSpatial spatial : items) { + selectSpatial(spatial); + return; + } + } + + private void setSceneInfo(final JmeNode jmeNode, final boolean active) { + final NavMeshTopComponent inst = this; + java.awt.EventQueue.invokeLater(new Runnable() { + + public void run() { + if (jmeNode != null) { + } else { + } + + if (!active) { + result.removeLookupListener(inst); + close(); + } else { + open(); + requestActive(); + } + } + }); + } + + protected void addSaveNode(org.openide.nodes.Node node) { + setActivatedNodes(new org.openide.nodes.Node[]{node}); + } + + private void cleanupControllers() { + if (cameraController != null) { + cameraController.disable(); + cameraController = null; + } + if (toolController != null) { + toolController.cleanup(); + toolController = null; + } + if (editorController != null) { + editorController.cleanup(); + editorController = null; + } + setActivatedNodes(new org.openide.nodes.Node[]{}); + } + + private void selectSpatial(JmeSpatial spatial) { + selectedSpat = spatial; + } + + private void generateNavMesh() { + float cellHeight = Float.parseFloat(cellHeightField.getText()); + float cellSize = Float.parseFloat(cellSizeField.getText()); + boolean clipLedges = clipLedgesBox.isSelected(); + float contourMaxDeviation = Float.parseFloat(contourMaxDeviationField.getText()); + float contourSampleDistance = Float.parseFloat(contourSampleDistanceField.getText()); + float edgeMaxDeviation = Float.parseFloat(edgeMaxDeviationField.getText()); + float maxEdgeLength = Float.parseFloat(maxEdgeLengthField.getText()); + float maxTraversableSlope = Float.parseFloat(maxTraversableSlopeField.getText()); + float maxTraversableStep = Float.parseFloat(maxTraversableStepField.getText()); + float maxVertsPerPoly = Float.parseFloat(maxVertsPerPolyField.getText()); + float mergeRegionSize = Float.parseFloat(mergeRegionSizeField.getText()); + float minTraversableHeight = Float.parseFloat(minTraversableHeightField.getText()); + float smoothingThreshold = Float.parseFloat(smoothingThresholdField.getText()); + float traversableAreaBorderSize = Float.parseFloat(traversableAreaBorderSizeField.getText()); + boolean useConservativeExpansion = useConservativeExpansionBox.isSelected(); + float minUnconnectedRegionSize = Float.parseFloat(minUnconnectedRegionSizeField.getText()); + + Mesh debugMesh = editorController.generateNavMesh(cellSize, cellHeight, minTraversableHeight, + maxTraversableStep, maxTraversableSlope, + clipLedges, traversableAreaBorderSize, + smoothingThreshold, useConservativeExpansion, + minUnconnectedRegionSize, mergeRegionSize, + maxEdgeLength, edgeMaxDeviation, maxVertsPerPoly, + contourSampleDistance, contourMaxDeviation); + + toolController.attachNavMesh(debugMesh); + } + + + private void initUIFields() { + NavMeshGenerator generator = editorController.getNavMeshGenerator(); + if (generator != null) { + cellSizeField.setText(""+generator.getCellSize()); + cellHeightField.setText(""+generator.getCellHeight()); + minTraversableHeightField.setText(""+generator.getMinTraversableHeight()); + maxTraversableStepField.setText(""+generator.getMaxTraversableStep()); + maxTraversableSlopeField.setText(""+generator.getMaxTraversableSlope()); + clipLedgesBox.setSelected(generator.isClipLedges()); + traversableAreaBorderSizeField.setText(""+generator.getTraversableAreaBorderSize()); + smoothingThresholdField.setText(""+generator.getSmoothingThreshold()); + useConservativeExpansionBox.setSelected(generator.isUseConservativeExpansion()); + minUnconnectedRegionSizeField.setText(""+generator.getMinUnconnectedRegionSize()); + mergeRegionSizeField.setText(""+generator.getMergeRegionSize()); + maxEdgeLengthField.setText(""+generator.getMaxEdgeLength()); + edgeMaxDeviationField.setText(""+generator.getEdgeMaxDeviation()); + maxVertsPerPolyField.setText(""+generator.getMaxVertsPerPoly()); + contourSampleDistanceField.setText(""+generator.getContourSampleDistance()); + contourMaxDeviationField.setText(""+generator.getContourMaxDeviation()); + } + } + + @Override + public void sceneOpened(SceneRequest request) { + if (request.equals(currentRequest)) { + Logger.getLogger(NavMeshTopComponent.class.getName()).finer("Terrain sceneRequested " + request.getWindowTitle()); + + setSceneInfo(currentRequest.getJmeNode(), true); + + if (cameraController != null) { + cameraController.disable(); + } + if (toolController != null) { + toolController.cleanup(); + } + + //for (int i=0; i + + + + + + + + diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponentWstcref.xml b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponentWstcref.xml new file mode 100644 index 000000000..637901568 --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/NavMeshTopComponentWstcref.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/layer.xml b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/layer.xml new file mode 100644 index 000000000..154321511 --- /dev/null +++ b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/layer.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/logo.png b/sdk/jme3-navmesh-gen/src/com/jme3/gde/nmgen/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5889730133b169da3e7b3bc1806e893c230572aa GIT binary patch literal 3403 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 literal 0 HcmV?d00001 diff --git a/sdk/jme3-templates/src/com/jme3/gde/templates/BasicGameProject.zip b/sdk/jme3-templates/src/com/jme3/gde/templates/BasicGameProject.zip index 15285646c8bcb93fdc6cf878afa507734e305356..7a056e1e9ada84108f312c1df476a4b97cb2dc15 100644 GIT binary patch delta 402 zcmdn5vtNfdz?+$civa{yJ58C$tHcbV`XnZLiGW#&S`YypFtt|`LP>}OC#UA67VD$x zPL!Ce%P0g@GC7J-5X79E$EXda=P;_O0{Qk}%b)}ckS;DtMm1;hGe$MA4t^#DFm1#X z0HQayG6}PS8ME1Y8Nm!~P74Sljf<59ERx5^4-skL{|IJGE)g^W$xL1;XalD22uV+7 z7LtdGNl$hVQUtRX0LANtzeWEH+u#6ilBM)(6vnh0VaUnTP?H i&J=<07m1jF`8P$Jz_gsG2Si@f9ZYW)bpX@fL_GmjmVwFu delta 402 zcmdn5vtNfdz?+$civa|Jy9FomDlvnoK8cB5B4Adc7DPY?OzqW#P!b}+$*Fm%#rmka z6D20=G73SJOpam{1TiP)F={in$YoBR!>FnXdL9fTCY>;*vadLcO|+Y+kQ3NB|N43P^Jwg&rWval&w?zFHznEoql q2BytK48U}z2!y{##01R0DdGgC