Projected Textures

Started by JavaMan, March 13, 2008, 03:47:53 PM

Previous topic - Next topic

JavaMan

How do I get this affect? How the texture appears to move over the bump there.

Could I have a little example, or maybe what class(es) is used for this?
JMan

EgonOlsen

You create an instance of Projector that defines the projection. It works exactly like any camera would do. Then you assign this projector to a texture. From there on, this texture will be projected on all objects that use the texture. The normal texture coordinates will be ignored in this case.
Basically, that's all...i hope it helps.

JavaMan

Hey, thanks. I don't know if I could have figured that out. I needed it for terrain creation in the object3d editor.

I am still playing around with it, but I think I'll get it.
Jman

JavaMan

Hi, I was experimenting, and I have the projected texture onto the Object, but it looks strange.
Here is the texture i am projecting onto a plane and a sphere.


Here is the texture projected onto the plane and the sphere.

Why is it disfigured like that? The projector is looking straight down the Y axis from 250,0,500.
Does it have to do with the angle of the projector? How do I get it to project the texture how it looks?
Thanks,

Jman

Also, if it would make it simpler for you, and if you don't care about sharing its source. I would be fine to look at the source to the example I first showed.

EgonOlsen

#4
I'll see if i can find the sources later.

Edit: This seems to be it. I'm not sure if it's the latest version, but anyway:


import java.awt.*;
import java.awt.event.*;
import com.threed.jpct.*;
import com.threed.jpct.util.*;
import com.threed.jpct.procs.*;
import java.io.*;
import org.lwjgl.input.*;

public class ProjectiveDemo implements IPaintListener {

    private KeyMapper keyMapper = null;
    private FrameBuffer fb = null;
    private World world = null;
    private Object3D plane = null;
    private Object3D ramp = null;
    private Object3D cube = null;
    private Object3D sphere = null;
    private int fps = 0;
    private long time = System.currentTimeMillis();
    private Projector[] projectors = new Projector[3];
    private MouseMapper mouse = null;
    private float xAngle = 0;
    private boolean doloop = true;
    private boolean bloomy = false;

    public ProjectiveDemo() {
        Config.maxPolysVisible = 50000;
        Config.glColorDepth = 24;
        Config.glVertexArrays = true;
        Config.glTriangleStrips = true;
        Config.glFullscreen = false;
    }

    public void finishedPainting() {
        if (System.currentTimeMillis() - time >= 1000) {
            System.out.println(fps);
            fps = 0;
            time = System.currentTimeMillis();
        }
        fps++;
    }

    public void startPainting() {

    }

    private void initStuff() throws Exception {
        fb = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_NORMAL);
        world = new World();
        fb.enableRenderer(IRenderer.RENDERER_OPENGL, IRenderer.MODE_OPENGL);
        fb.disableRenderer(IRenderer.RENDERER_SOFTWARE);
        keyMapper = new KeyMapper();
        fb.setPaintListener(this);

        plane = Primitives.getPlane(20, 15);
        ramp = loadASC("city.asc", 1.5f);
        sphere = Primitives.getSphere(30);
        sphere.translate( -50, 10, 50);
        cube = Primitives.getCube(20);
        cube.translate(60, -20, 60);

        plane.rotateX((float) Math.PI / 2f);

        world.addObject(plane);
        world.addObject(ramp);
        world.addObject(sphere);
        world.addObject(cube);

        TextureManager tm = TextureManager.getInstance();
        Texture red = loadTexture("textures/red_spot.gif");
        Texture green = loadTexture("textures/sailing.png");
        Texture blue = loadTexture("textures/blue_spot.gif");
        Texture stones = loadTexture("textures/stones.jpg");
        Texture t5 = loadTexture("textures/bumps.jpg");
        tm.addTexture("red", red);
        tm.addTexture("green", green);
        tm.addTexture("blue", blue);
        tm.addTexture("stones", stones);
        tm.addTexture("bumps", t5);

        TextureInfo objTexs = new TextureInfo(tm.getTextureID("bumps"));
        objTexs.add(tm.getTextureID("red"), TextureInfo.MODE_ADD);
        objTexs.add(tm.getTextureID("green"), TextureInfo.MODE_ADD);
        objTexs.add(tm.getTextureID("blue"), TextureInfo.MODE_ADD);

        TextureInfo planeTexs = new TextureInfo(tm.getTextureID("stones"));
        planeTexs.add(tm.getTextureID("red"), TextureInfo.MODE_ADD);
        planeTexs.add(tm.getTextureID("green"), TextureInfo.MODE_ADD);
        planeTexs.add(tm.getTextureID("blue"), TextureInfo.MODE_ADD);

        for (int i = 0; i < projectors.length; i++) {
            projectors[i] = new Projector();
        }
        red.setProjector(projectors[0], false);
        green.setProjector(projectors[1], false);
        blue.setProjector(projectors[2], false);

        plane.setTexture(planeTexs);
        ramp.calcTextureWrapSpherical();
        ramp.setTexture(objTexs);
        cube.calcTextureWrapSpherical();
        cube.setTexture(objTexs);
        sphere.calcTextureWrapSpherical();
        sphere.setTexture(objTexs);

        Light light = new Light(world);
        light.setPosition(new SimpleVector(0, -40, 0));
        light.setIntensity(40, 45, 42);

        world.setAmbientLight(20, 20, 20);

        world.buildAllObjects();

        mouse = new MouseMapper(fb);
        mouse.hide();
    }

    private Texture loadTexture(String tex) throws Exception {
        SimpleStream s = new SimpleStream(tex);
        Texture t = new Texture(s.getStream());
        s.close();
        return t;
    }

    private Object3D loadASC(String asc, float scale) throws Exception {
        SimpleStream s = new SimpleStream(asc);
        Object3D o = Loader.loadASC(s.getStream(), scale, false);
        s.close();
        return o;
    }

    private void checkKeys() {
        move();
        KeyState ks = null;
        while ((ks = keyMapper.poll()) != KeyState.NONE) {
            if (ks.getKeyCode() == KeyEvent.VK_ESCAPE) {
                doloop = false;
            }
            if (ks.getKeyCode() == KeyEvent.VK_B && ks.getState()) {
                bloomy = !bloomy;
            }

            if (ks.getKeyCode() == KeyEvent.VK_SPACE && ks.getState()) {
                TextureManager.getInstance().getTexture("green").setEnabled(
                        !TextureManager.getInstance().getTexture("green")
                        .isEnabled());
            }

            if (ks.getKeyCode() == KeyEvent.VK_UP) {
                world.getCamera().moveCamera(Camera.CAMERA_MOVEIN, 5f);
            }

            if (ks.getKeyCode() == KeyEvent.VK_DOWN) {
                world.getCamera().moveCamera(Camera.CAMERA_MOVEOUT, 5f);
            }

            if (ks.getKeyCode() == KeyEvent.VK_LEFT) {
                world.getCamera().moveCamera(Camera.CAMERA_MOVELEFT, 5f);
            }

            if (ks.getKeyCode() == KeyEvent.VK_RIGHT) {
                world.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT, 5f);
            }

            if (ks.getKeyCode() == KeyEvent.VK_PAGE_UP) {
                world.getCamera().moveCamera(Camera.CAMERA_MOVEUP, 5f);
            }

            if (ks.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
                world.getCamera().moveCamera(Camera.CAMERA_MOVEDOWN, 5f);
            }

        }
    }

    private void move() {
        Matrix rot = world.getCamera().getBack();
        int dx = mouse.getDeltaX();
        int dy = mouse.getDeltaY();

        float ts = 0.2f;
        float tsy = ts;

        if (dx != 0) {
            ts = Math.abs(dx) / -500f;
        }
        if (dy != 0) {
            tsy = Math.abs(dy) / 500f;
        }

        if (dx < 0) {
            rot.rotateAxis(rot.getYAxis(), ts);
        }

        if (dx > 0) {
            rot.rotateAxis(rot.getYAxis(), -ts);
        }

        if (dy > 0 && xAngle < Math.PI / 4.2) {
            rot.rotateX(tsy);
            xAngle += tsy;
        }
        if (dy < 0 && xAngle > -Math.PI / 4.2) {
            rot.rotateX( -tsy);
            xAngle -= tsy;
        }
    }

    private void doIt() throws Exception {
        Camera cam = world.getCamera();
        cam.moveCamera(Camera.CAMERA_MOVEOUT, 150);
        cam.moveCamera(Camera.CAMERA_MOVEUP, 100);
        cam.lookAt(plane.getTransformedCenter());

        BloomGLProcessor bloom = new BloomGLProcessor();
        fb.addPostProcessor(bloom);

        for (int i = 0; i < projectors.length; i++) {
            projectors[i].setPosition(plane.getTransformedCenter());
            projectors[i].moveCamera(Camera.CAMERA_MOVEUP, 100);
            projectors[i].setFOVLimits(0, 1);
            projectors[i].setFOV(0.2f);
            projectors[i].rotateCameraX((float) Math.PI / 2f);
        }

        float a = 0;
        while (doloop && !org.lwjgl.opengl.Display.isCloseRequested()) {
            checkKeys();
            fb.clear(Color.BLACK);
            a += 0.01f;

            for (int i = 0; i < projectors.length; i++) {
                SimpleVector lookAt = new SimpleVector(projectors[i].getPosition());
                SimpleVector pivot = new SimpleVector(lookAt);
                float offset = 10 + (5 * i);
                pivot.y += offset;
                lookAt.add(new SimpleVector(0, offset, 10));
                lookAt = lookAt.calcSub(pivot);
                if ((i & 1) == 1) {
                    lookAt.rotateY((float) a * (1f + 0.3f * i));
                } else {
                    lookAt.rotateY((float) - a * (1f + 0.3f * i));
                }
                lookAt.add(pivot);
                projectors[i].lookAt(lookAt);
            }

            world.renderScene(fb);
            world.draw(fb);
            if (bloomy) {
                fb.runPostProcessors();
            }
            fb.update();
            fb.displayGLOnly();
            Thread.sleep(10);
        }
        fb.disableRenderer(IRenderer.RENDERER_OPENGL);
        fb.dispose();
        System.exit(0);
    }

    public static void main(String[] args) throws Exception {
        Config.glVerbose = true;
        ProjectiveDemo cd = new ProjectiveDemo();
        cd.initStuff();
        cd.doIt();
    }

    public class SimpleStream {
        private InputStream stream;

        public SimpleStream(String file) {
            stream = this.getClass().getClassLoader().getResourceAsStream(file);
        }

        public InputStream getStream() {
            return stream;
        }

        public void close() throws IOException {
            stream.close();
        }
    }

    class MouseMapper {

        private boolean hidden = false;

        private int height = 0;

        public MouseMapper(FrameBuffer buffer) {
            height = buffer.getOutputHeight();
            init();
        }

        public void hide() {
            if (!hidden) {
                Mouse.setGrabbed(true);
                hidden = true;
            }
        }

        public void show() {
            if (hidden) {
                Mouse.setGrabbed(false);
                hidden = false;
            }
        }

        public boolean isVisible() {
            return!hidden;
        }

        public void destroy() {
            show();
            if (Mouse.isCreated()) {
                Mouse.destroy();
            }
        }

        public boolean buttonDown(int button) {
            return Mouse.isButtonDown(button);
        }

        public int getMouseX() {
            return Mouse.getX();
        }

        public int getMouseY() {
            return height - Mouse.getY();
        }

        public int getDeltaX() {
            if (Mouse.isGrabbed()) {
                return Mouse.getDX();
            } else {
                return 0;
            }
        }

        public int getDeltaY() {
            if (Mouse.isGrabbed()) {
                return Mouse.getDY();
            } else {
                return 0;
            }
        }

        private void init() {
            try {
                if (!Mouse.isCreated()) {
                    Mouse.create();
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}


JavaMan

#5
Hi, I don't mean to seem helpless with this, but I compiled the code how it is, created some fake textures to use in place, and removed the ramp method calls, because I don't have any ASC files. This is what the program looks like.


I removed the multi texturing and this is what it looks like


Those textures are what is what is being applied.
I don't understand why the multi texturing disappears like that because the console prints out Using OpenGl with 2 texture stages. I ran it on a laptop that can do 4 stages, and the same result. I tried out setting
Config.maxTextureLayers to 2 layers, but still the same results.  ??? ??? ??? ??? ??? ???

The example on the site for the projected textures runs fine, obviously I got a screenshot.

EgonOlsen

Hard to tell...are you by any change adding textures in the higher stages that are all white?

JavaMan

No, they are all color. If you want, I can post images of them. Do you have the source for the version of demo program that is on the site?

EgonOlsen

The code works as expected. It's not the demo's code but the only difference is the changed wider fov of the projectors that the demo uses. I've just compiled the code on a "clean" machine (i.e. with no jPCT related stuff on it before) and it works fine.
Maybe your textures are single colored squares in blue, red and green that stack up to white? Remember that the texture is clamped to edge, i.e. everything out of the projectors view will be textured like the last line in the texture. Try to add a black border around your textures and see if that helps.

JavaMan

Hey, thanks. I had textures with a blue dot and the surrounding was white. So, that was the problem.
I have just a little question about transparency and projection. Here I have a plane, cube, and sphere all set with a textureinfo and the red dot moving around. The plane does not have any transparency set.


Here is the same example with the plane set to a transparency of 0.


The plane seems to disappear totally from view when the projector does not shine on it. Is this how it is supposed to work? It isn't a problem for me, I was just wondering.
Jman

Hey, by the way, thanks for always helping me out.  ;D

EgonOlsen

Quote from: JavaMan on March 16, 2008, 02:44:24 AM
Is this how it is supposed to work?
Yes and no...it's caused by the alpha channel of the projectors being modulated with the plane's one. So the plane is visible only when the result of this operation is !=0, which is only true if all three projectors overlap.
In the final 1.16, i'll change this behaviour in a way that the alpha channel inherits the mode from the texture blending (unless you disable it via a Config-switch or the hardware doesn't support it). I think this behaviour is more what one would expect.

JavaMan

QuoteYes and no...it's caused by the alpha channel of the projectors being modulated with the plane's one. So the plane is visible only when the result of this operation is !=0, which is only true if all three projectors overlap.
In the final 1.16, i'll change this behaviour in a way that the alpha channel inherits the mode from the texture blending (unless you disable it via a Config-switch or the hardware doesn't support it). I think this behaviour is more what one would expect.

Thats interesting. Its not the behavior I think that would be expected, but it could be an interesting effect for easily creating a spotlight.

Thanks for the help :))