Projective texture

to Joseph Beaufils

i'm kill this problem by remove draw fps string from game example

also it can be fixed by this code:


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

package com.jme.scene;

import java.io.IOException;
import java.util.Stack;

import com.jme.app.SimpleGame;
import com.jme.image.Texture;
import com.jme.intersection.CollisionResults;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;

/**
 *
 * <code>Text</code> allows text to be displayed on the screen. The
 * renderstate of this Geometry must be a valid font texture.
 *
 * @author Mark Powell
 * @version $Id: Text.java,v 1.27 2006/08/28 01:37:03 sunsett Exp $
 */
public class Text extends Geometry {

    private static final long serialVersionUID = 1L;

    private StringBuffer text;

    private ColorRGBA textColor = new ColorRGBA();

    /**
     * The compiled list of renderstates for this geometry, taking into account
     * ancestors' states - updated with updateRenderStates()
     */
    public RenderState[] states = new RenderState[RenderState.RS_MAX_STATE];

    public Text() {}
   
    /**
     * Creates a texture object that starts with the given text.
     *
     * @see com.jme.util.TextureManager
     * @param name
     *            the name of the scene element. This is required for
     *            identification and comparision purposes.
     * @param text
     *            The text to show.
     */
    public Text(String name, String text) {
        super(name);
        setCullMode(SceneElement.CULL_NEVER);
        this.text = new StringBuffer(text);
        setRenderQueueMode(Renderer.QUEUE_ORTHO);
    }

    /**
     *
     * <code>print</code> sets the text to be rendered on the next render
     * pass.
     *
     * @param text
     *            the text to display.
     */
    public void print(String text) {
        this.text.replace(0, this.text.length(), text);
    }

    /**
     * Sets the text to be rendered on the next render. This function is a more
     * efficient version of print(String).
     *
     * @param text
     *            The text to display.
     */
    public void print(StringBuffer text) {
        this.text.setLength(0);
        this.text.append(text);
    }

    /**
     *
     * <code>getText</code> retrieves the text string of this
     * <code>Text</code> object.
     *
     * @return the text string of this object.
     */
    public StringBuffer getText() {
        return text;
    }

    /**
     * <code>draw</code> calls super to set the render state then calls the
     * renderer to display the text string.
     *
     * @param r
     *            the renderer used to display the text.
     */
    public void draw(Renderer r) {
        //if (!r.isProcessingQueue()) {
        //    if (r.checkAndAdd(this)) return;
        //}
        //super.draw(r);
        //r.draw(this);
    }

    /**
     * Sets the color of the text.
     *
     * @param color
     *            Color to set.
     */
    public void setTextColor(ColorRGBA color) {
       textColor = color;
    }

    /**
     * Returns the current text color.
     *
     * @return Current text color.
     */
    public ColorRGBA getTextColor() {
        return textColor;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.jme.scene.Spatial#hasCollision(com.jme.scene.Spatial,
     *      com.jme.intersection.CollisionResults)
     */
    public void findCollisions(Spatial scene, CollisionResults results) {
        //Do nothing.
    }

    public boolean hasCollision(Spatial scene, boolean checkTriangles) {
        return false;
    }

    public float getWidth() {
        float rVal = 10f * text.length() * worldScale.x;
        return rVal;
    }

    public float getHeight() {
        float rVal = 16f * worldScale.y;
        return rVal;
    }

    /**
     * @return a Text with {@link #DEFAULT_FONT} and correct alpha state
     * @param name name of the spatial
     */
    public static Text createDefaultTextLabel( String name ) {
        return createDefaultTextLabel( name, "" );
    }

    /**
     * @return a Text with {@link #DEFAULT_FONT} and correct alpha state
     * @param name name of the spatial
     */
    public static Text createDefaultTextLabel( String name, String initialText ) {
        Text text = new Text( name, initialText );
        text.setCullMode( SceneElement.CULL_NEVER );
        text.setRenderState( getDefaultFontTextureState() );
        text.setRenderState( getFontAlpha() );
        return text;
    }

    /*
    * @return an alpha states for allowing 'black' to be transparent
    */
    private static AlphaState getFontAlpha() {
        AlphaState as1 = DisplaySystem.getDisplaySystem().getRenderer().createAlphaState();
        as1.setBlendEnabled( true );
        as1.setSrcFunction( AlphaState.SB_SRC_ALPHA );
        as1.setDstFunction( AlphaState.DB_ONE );
        as1.setTestEnabled( true );
        as1.setTestFunction( AlphaState.TF_GREATER );
        return as1;
    }

    /**
     * texture state for the default font.
     */
    private static TextureState defaultFontTextureState;

    public static final void resetFontTexture() {
        defaultFontTextureState = null;
    }
   
    /**
     * A default font cantained in the jME library.
     */
    public static final String DEFAULT_FONT = "com/jme/app/defaultfont.tga";
   
    protected void applyRenderState(Stack[] states) {
        for (int x = 0; x < states.length; x++) {
            if (states[x].size() > 0) {
                this.states[x] = ((RenderState) states[x].peek()).extract(
                        states[x], this);
            } else {
                this.states[x] = Renderer.defaultStateList[x];
            }
        }
    }

    /**
     * Creates the texture state if not created before.
     * @return texture state for the default font
     */
    private static TextureState getDefaultFontTextureState() {
        if ( defaultFontTextureState == null ) {
            defaultFontTextureState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
            defaultFontTextureState.setTexture( TextureManager.loadTexture( SimpleGame.class
                    .getClassLoader().getResource( DEFAULT_FONT ), Texture.MM_LINEAR,
                    Texture.FM_LINEAR ) );
            defaultFontTextureState.setEnabled( true );
        }
        return defaultFontTextureState;
    }
   
    public void write(JMEExporter e) throws IOException {
        super.write(e);
        OutputCapsule capsule = e.getCapsule(this);
        capsule.write(text.toString(), "textString", "");
        capsule.write(textColor, "textColor", new ColorRGBA());
    }

    public void read(JMEImporter e) throws IOException {
        super.read(e);
        InputCapsule capsule = e.getCapsule(this);
        text = new StringBuffer(capsule.readString("textString", ""));
        textColor = (ColorRGBA)capsule.readSavable("textColor", new ColorRGBA());
       
    }
}

So from what I see above (try to narrow it to just the important lines next time) you are cutting out Text's draw call?

yes

it's needed for old video card (internal Intell)

for new card's all work good

My card is newer than that  :wink:



I removed the FPSNode by replacing it with an empty node and I still have the problem of Stack underflow.



Here is my code for each problem in the ProjectedTextureUtil class :



UpVector :


   public static void matrixLookAt( Vector3f location, Vector3f at, Matrix4f result ) {
      localDir.set( at ).subtractLocal( location ).normalizeLocal();
      localDir.cross( Vector3f.UNIT_Y, localLeft );
      localLeft.cross( localDir, localUp );

localUp = new Vector3f(1,0,0);

      GL11.glPushMatrix();
...



instead of using the defaul localUp.

Projection :

   public static void updateProjectedTexture( Texture texture, float fov, float aspect, float near, float far, Vector3f pos, Vector3f aim ) {
//      matrixPerspective( fov, aspect, near, far, lightProjectionMatrix );
matrixProjection( fov, aspect, near, far, lightProjectionMatrix );
      matrixLookAt( pos, aim, lightViewMatrix );
      texture.getMatrix().set( lightViewMatrix.multLocal( lightProjectionMatrix ).multLocal( biasMatrix ) );
   }



which generate the Stack underflow error.

the fpsnode has nothing to do with this, as far as i can imagine…


  1. modified the updateProjectedTexture method to take a user specified up-vector, so now you can specify your own like (1,0,0) like you wanted…


  2. misplaced the push/pop matrix methods, fixed…



    all checked into cvs…

You are really fast, thank you !!!  :lol:



Sorry to bother you with all my problems  :expressionless:

hey MrCoder :slight_smile:



first, thx for writing this code … a great feature for me, the projected texture thingy!!!



second, i have some questions which you will maybe be willing to answer:

  • does this feature use some special opengl features? because it behaves really strange for me.

      it runs smooth on my desktop machine (if i remove the terrain from the example i even get a high fps). on my laptop it crashes (vm-crash with some errors in a .dll). on the minimac (with bootcamp running winxp) it only works if i add ONLY the projected textures to the render state.



    in the original example it was (for the box (i removed the terrain)):



    ts.setTexture( t0, 0 );

    ts.setTexture( projectedTexture1, 1 );

    ts.setTexture( projectedTexture2, 2 );

    dummyBox.setRenderState( ts );

    ^^ which gives me really strange artefacts (kinda psycodelic which is good but … :slight_smile: )



    ts.setTexture( projectedTexture1, 0 );

    ts.setTexture( projectedTexture2, 1 );

    dummyBox.setRenderState( ts );

    ^^ this one works as expected



    do you have any ideas about that?

    greetings sascha

You need the extension ARB_multitexture and a certain number of texture units to run the test… Depending on your card it might try to do software emulation and whatnot. So a check in the test for the requirements might fix the issue.

it seems that multitexturing in general is possible … only with the projectedTexture thing it doesnt work. what do you mean by software emulation? also what kind of requirements do you mean. do you think it's possible to tweak the graphiccard? its a Mobile Intel 945 Express Chipset with shared memory :confused:

i have actually had similar issues in the past, so i guess it's time to start digging into this again…

if it helps you … for me the order, in which you add the textures to the renderstate make a difference! so if you put the projected textures first or the "normal" textures first.

i could also offer some help with the java code/testing (though i dont know a lot about opengl itself).

Download this tool: http://www.realtech-vr.com/glview/download.html

Once you have the tool open, go to 'Report', click 'Copy to clipboard' and paste here

i can only do it on my desktop at home … but here it works just fine. i'm afraid i can't try it on the minimac before wednesday :confused:

Don't know if it helps but, I had also a problem with multitexturing and projector because my terrain already use 2 textures, which was the limit of my graphic card.

So I project the texture of the projector on a mesh, which has the same shape than the terrain and which is big enough to contain the projected texture.

hmm … sounds like a feasible workaround. i think i gonna try that one. thx!

I actualy thought this would be a crappy card which uses GDI Generic as OpenGL implementor… Turns out it's quite decent. Anyway, the test works on my RIVA TNT which is a very similar card (supports OpenGL 1.5 with latest driver), it only has 2 texture units, the test works but very slow (0-1 fps).



It might be bugging out because the test uses 3 texture units but your card only supports 2.

i have again a strange issue with the projectedTexture thingy. i have a huge amount of boxes which i position via physics. the renderstate of these boxes is defined as follows



projectedTexture = TextureManager.loadTexture(getClass().getClassLoader().getResource(
                "com/bitkid/wasteyard/textures/lioness.jpg"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
ProjectedTextureUtil.setupProjectedTexture(projectedTexture, Texture.WM_BCLAMP_S_BCLAMP_T, Texture.ACF_ADD);

for (File texFile : textureDirectory.listFiles()) {
            TextureState ts = game.getDisplay().getRenderer().createTextureState();
            Texture t = TextureManager.loadTexture(getClass().getClassLoader().getResource(
                    "com/bitkid/wasteyard/textures/metal/" + texFile.getName()), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
            t.setWrap(Texture.WM_WRAP_S_WRAP_T);
            ts.setTexture(t,0);
            ts.setTexture(projectedTexture,1);
            textureStates.add(ts);
}



in the update method i call
ProjectedTextureUtil.updateProjectedTexture(texture, 28.0f, 1.0f, 1.0f, 1000.0f, position, aim, Vector3f.UNIT_Y);
where position is cam.getLocation() and aim = cam.getDirection()

the result is as follows:




as far as i understand this wrong or? the edges of the projection should get warped with the shape of the objects, but i looks like it just renders the image parallel to cam plane.

aehm .. and another question .. which is here in the wrong place maybe. is there a easy way to convert a dynamicPhysicObject to a staticPhysicObject?

thx, sascha

edit: after playing around a bit .. i'm not so sure anymore .. if the shown pictures are "wrong behaviour" :/

If you project from your eye, it will look like what you are showing.  If you project from another point than your eye, you might get a result more similar to what you are expecting.

renanse said:

If you project from your eye, it will look like what you are showing.  If you project from another point than your eye, you might get a result more similar to what you are expecting.


yup!

Sorry to bring up an old post, but I thought it a good thread.



Maybe someone can shed some light on this problem.  I have several tables that I am trying to use a projected texture on, each table has it's own texture state which I am adding to.  After the tables (and textureStates) are created they are placed into a holder node and translated to the correct position.



    protected void setLightTexture( int tableNumber ) {
       
        Vector3f position = Utils.convertVector( jme_Driver.getDriver().getLogic().getTable( tableNumber ).getPosition() );
        position.y += Globals.tableLightHeight;
       
        // position = new Vector3f( 0, Globals.tableLightHeight, 0 );
       
        System.out.println( "table: " + tableNumber + " t" + position );
       
        Texture projectedTexture1 = TextureManager.loadTexture( TestProjectedTexture.class.getClassLoader().getResource(
                 "jmetest/data/images/Monkey.png" ), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR );
       
       
       
        ProjectedTextureUtil.setupProjectedTexture( projectedTexture1, Texture.WM_BCLAMP_S_BCLAMP_T, Texture.ACF_ADD );
        ProjectedTextureUtil.updateProjectedTexture( projectedTexture1, 20.0f, 1.5f, 1.0f, 1000.0f, position, Vector3f.UNIT_X, Vector3f.UNIT_Y );
       
       
        tableTextureStates[ 0 ][ tableNumber ].setTexture( projectedTexture1, 1 );
        tableTextureStates[ 1 ][ tableNumber ].setTexture( projectedTexture1, 1 );
       
    }
 



and here is the result, notice that the texture is not the same on all the tables.


Can anyone shed some light on this issue?