How to do spell rings (like WC3) via ProjectedTexture

This code is to create “spell rings” like you see in RTS games like WC3 but can also be used for anything you need to use some sort of indicator on terrain with. It’s simply a modification of MrCoder’s example TestProjectedTexture.java found in jmetest/effects under the jME source.



Here’s a convenient video of what this effect does.



TextProjectedTextureMouse.java:


package testing;

import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.MouseInput;
import com.jme.intersection.TrianglePickResults;
import com.jme.math.Matrix4f;
import com.jme.math.Ray;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.scene.TriMesh;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.effects.ProjectedTextureUtil;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.jmex.terrain.util.ProceduralSplatTextureGenerator;

/**
 * <code>TestProjectedTexture</code>
 *
 * @author Tyler Trussell
 */
public class TestProjectedTextureMouse extends SimpleGame {
    private static final Logger logger = Logger
            .getLogger(TestProjectedTextureMouse.class.getName());

   private TerrainPage terrain;

   private Texture projectedTexture1;

    boolean mouseIgnore = false;

    private TrianglePickResults results;

   public static void main( String[] args ) {
      TestProjectedTextureMouse app = new TestProjectedTextureMouse();
      app.setConfigShowMode(ConfigShowMode.AlwaysShow);
      app.start();
   }

    @Override
   protected void simpleUpdate() {

        /*
         * This really long section of Vector3f and Vector2f
         * and pick results is to find where you are pointing
         * with your mouse. It then saves the Vector3f of where
         * you are pointing to the variable "loc"
         */
        Vector2f screenPos = new Vector2f(MouseInput.get().getXAbsolute(), MouseInput.get().getYAbsolute());
        Vector3f startPoint = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 0);
        Vector3f endPoint = DisplaySystem.getDisplaySystem().getWorldCoordinates(screenPos, 1);
        Ray ray = new Ray(startPoint, endPoint.subtract(startPoint));

        results.clear();
        rootNode.findPick(ray, results);

        Vector3f loc = new Vector3f();
        Vector3f[] vertex = new Vector3f[3];
        boolean foundMeshHit = false;
        for(int pickIndex = 0; pickIndex < results.getNumber(); pickIndex++)
        {
            TriMesh mesh = (TriMesh) results.getPickData(pickIndex).getTargetMesh();
            for (int j = 0; j < mesh.getTriangleCount(); j++)
            {
                mesh.getTriangle(j, vertex);

                foundMeshHit = (ray.intersectWhere(vertex[0].addLocal(mesh.getWorldTranslation()),
                    vertex[1].addLocal(mesh.getWorldTranslation()),
                    vertex[2].addLocal(mesh.getWorldTranslation()), loc));
                if(foundMeshHit)
                {

                    Vector3f newLoc = loc.clone();
                    newLoc.y += 20f;

                    Vector3f groundLoc = loc.clone();
                    groundLoc.y = terrain.getHeight(groundLoc);

                    ProjectedTextureUtil.updateProjectedTexture(projectedTexture1, 30.0f, 1.0f, 1.0f, 1000.0f, newLoc, groundLoc, new Vector3f(0,-1,0));
                }
            }
        }
   }

   protected void simpleInitGame()
    {
        input = new InputHandler();
        MouseInput.get().setCursorVisible(true);

        results = new TrianglePickResults();
       
      try {
            display.setTitle( "Mouse ProjectedTexture Test" );

         cam.getLocation().set( new Vector3f( 50, 50, 0 ) );
         cam.lookAt( new Vector3f(), Vector3f.UNIT_Y );

         CullState cs = display.getRenderer().createCullState();
         cs.setCullFace( CullState.Face.Back );
         cs.setEnabled( true );

         TextureState ts = display.getRenderer().createTextureState();

         //create terrain
         URL grayScale = TestProjectedTextureMouse.class.getClassLoader().getResource( "jmetest/data/texture/terrain.png" );
         ImageBasedHeightMap heightMap = new ImageBasedHeightMap( new javax.swing.ImageIcon( grayScale ).getImage() );
         Vector3f terrainScale = new Vector3f( .5f, .05f, .5f );
         terrain = new TerrainPage( "image icon", 33, (heightMap.getSize()) + 1, terrainScale, heightMap.getHeightMap() );
         terrain.setDetailTexture( 1, 16 );
         terrain.setModelBound( new BoundingBox() );
         terrain.updateModelBound();
         terrain.setLocalTranslation( new Vector3f( 0, 0, 0 ) );
         rootNode.attachChild( terrain );
         rootNode.setRenderState( cs );

         ProceduralSplatTextureGenerator pst = new ProceduralSplatTextureGenerator( heightMap );
         pst.addTexture( new javax.swing.ImageIcon( TestProjectedTextureMouse.class.getClassLoader().getResource(
               "jmetest/data/texture/grassb.png" ) ), -128, 0, 128 );
         pst.addTexture( new javax.swing.ImageIcon( TestProjectedTextureMouse.class.getClassLoader().getResource(
               "jmetest/data/texture/dirt.jpg" ) ), 0, 128, 255 );
         pst.addTexture( new javax.swing.ImageIcon( TestProjectedTextureMouse.class.getClassLoader().getResource(
               "jmetest/data/texture/highest.jpg" ) ), 128, 255, 384 );

         pst.addSplatTexture( new javax.swing.ImageIcon( TestProjectedTextureMouse.class.getClassLoader().getResource(
               "jmetest/data/texture/terrainTex.png" ) ), new javax.swing.ImageIcon( TestProjectedTextureMouse.class.getClassLoader().getResource(
               "jmetest/data/texture/water.png" ) ) );
         pst.createTexture( 512 );

         ts = display.getRenderer().createTextureState();
         ts.setEnabled( true );
         Texture t1 = TextureManager.loadTexture( pst.getImageIcon().getImage(), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true );
         ts.setTexture( t1, 0 );

         t1.setApply( Texture.ApplyMode.Combine );
         t1.setCombineFuncRGB( Texture.CombinerFunctionRGB.Modulate );
         t1.setCombineSrc0RGB( Texture.CombinerSource.CurrentTexture );
         t1.setCombineOp0RGB( Texture.CombinerOperandRGB.SourceColor );
         t1.setCombineSrc1RGB( Texture.CombinerSource.PrimaryColor );
         t1.setCombineOp1RGB( Texture.CombinerOperandRGB.SourceColor );
         t1.setCombineScaleRGB( Texture.CombinerScale.One );

         //create a texture to use for projection
         projectedTexture1 = TextureManager.loadTexture(
                    TestProjectedTextureMouse.class.getClassLoader().getResource(
               "jmetest/data/images/Monkey.png" ),
                    Texture.MinificationFilter.Trilinear,
                    Texture.MagnificationFilter.Bilinear );

         ts.setTexture( projectedTexture1, 1 );

         //this is were we set the texture up for projection
            projectedTexture1.setMatrix(new Matrix4f());
            projectedTexture1.setWrap(Texture.WrapMode.BorderClamp );
            projectedTexture1.setEnvironmentalMapMode( Texture.EnvironmentalMapMode.EyeLinear );
            projectedTexture1.setApply( Texture.ApplyMode.Combine );
            projectedTexture1.setCombineFuncRGB( Texture.CombinerFunctionRGB.Add );
            projectedTexture1.setCombineSrc0RGB( Texture.CombinerSource.CurrentTexture );
            projectedTexture1.setCombineOp0RGB( Texture.CombinerOperandRGB.SourceColor );
            projectedTexture1.setCombineSrc1RGB( Texture.CombinerSource.Previous );
            projectedTexture1.setCombineOp1RGB( Texture.CombinerOperandRGB.SourceColor );
            projectedTexture1.setCombineScaleRGB( Texture.CombinerScale.One );

         terrain.setRenderState( ts );

         terrain.lock();

         rootNode.setRenderQueueMode( com.jme.renderer.Renderer.QUEUE_OPAQUE );
      } catch( Exception e ) {
         logger.logp(Level.SEVERE, this.getClass().toString(),
                    "simpleInitGame()", "Exception", e);
      }
   }
}

nice job! :smiley: