Q3 maps

"renanse" wrote:
it's true that only one texturestate really affects a given trimesh at a given time. You could use an image that contains multiple subimages though and adjust the texture coordinates to use the exact subimages you require in the right places.
Well, most of the maps I found use this feature already to cram more subtextures into one image.

ah great, then you should be able to make some good use of that.

Batman: So you’ve coded BSP trees and BSPnodes already?

"renanse" wrote:
On the whole memory issue, let's break down a Trimesh class into it's actual components and see where the memory is used. The main difference I see between no arg and the name arg methods of construction is a handful of array and object creations. How big is a typical Vector3f or Quaternion? Or how big (memory wise) is an array that has a length of say 8 but all indexes are null?
I temporarily made Geometry a nonabstract class. It occupies 695 bytes per instance, so it is the memory consumer, not the TriMesh.
A single Vector3f takes 23 bytes (it contains 3 floats of 4 bytes each, which leaves 11 bytes for the class data itself). A quaternion takes 23 bytes, too. A Vector3f array of 10 takes 53 bytes, an arry of 20 takes 95 bytes. So it is 4 bytes per array entry + 11 bytes for the array itself, maybe with an additional (alignment) overhead every x entries.
I will try to profile the creation of a single TriMesh, may I'll find something interesting. I let you know.

A quat and vector3f shouldn’t take the same amount of memory(?)

"Cep21" wrote:
Batman: So you've coded BSP trees and BSPnodes already?
I had originally portedl Portal, ConvexRegion, ConvexRegionManager and BspNode. Then I found that it was not completely necessary to create the BspNode - the portal code works without it. I still have a more or less ported version of BspNode - some parts commented out because Camera did not implement a specific method public float getMaxCosSqrFrustumAngle() (which I have ported too, btw.).

I can give it all to you.
"Cep21" wrote:
A quat and vector3f shouldn't take the same amount of memory(?)
Why not? The quat has 4 floats, the vector 3, but it may be due to alignment in the vm...

Correction btw. If I comment out the creation of the DisplaySystem, which I do not need for Vector3f and Quaternion, both are said to be 24 bytes long. Remember the Sizeof class is guessing the size by inspecting garbage collector output.

Just the BSP node would be nice.

OK, here we go


package com.jme.scene;

import com.jme.math.Plane;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.Renderer;

public class BSPNode extends Node {
    // attributes
    protected Plane modelPlane;

    protected Plane worldPlane;

    // constructors

    /**
     * Empty Constructor to be used internally only.
     */
    public BSPNode() {
    }

    /**
     * Constructor instantiates a new <code>BSPNode</code>.
     *
     * @param name
     *            the name of the scene element. This is required for identification and comparision
     *            purposes.
     */
    public BSPNode(String name) {
        super(name);
    }

    // public methods

    /**
     * BSP is a binary tree. Use this method to attach the left child.
     *
     * @param child
     *            The new left child.
     * @return The old child at the index.
     */
    public Spatial attachLeftChild(Spatial child) {
        return setChild(0, child);
    }

    /**
     * BSP is a binary tree. Use this method to attach the right child.
     *
     * @param child
     *            The new right child.
     * @return The old child at the index.
     */
    public Spatial attachRightChild(Spatial child) {
        return setChild(2, child);
    }

    /**
     * Detach the left child.
     *
     * @return The child at the left position.
     */
    public Spatial detachLeftChild() {
        return detachChildAt(0);
    }

    /**
     * Detach the right child.
     *
     * @return The child at the right position.
     */
    public Spatial detachRightChild() {
        return detachChildAt(2);
    }

    /**
     * Getter for the left child.
     *
     * @return The left child.
     */
    public Spatial getLeftChild() {
        return getChild(0);
    }

    /**
     * Getter for the right child.
     *
     * @return The right child.
     */
    public Spatial getRightChild() {
        return getChild(2);
    }

    /**
     * Determine the bsp node whose represented region contains the point.
     *
     * @param point
     *            The point which is requested.
     * @return The BSP node referencing the requested region.
     */
    public BSPNode getContainingNode(Vector3f point) {
        BSPNode reply = null;
        Spatial left = getLeftChild();
        Spatial right = getRightChild();

        if (worldPlane.whichSide(point) == Plane.NEGATIVE_SIDE) {
            if (right != null && right instanceof BSPNode) {
                reply = ((BSPNode)right).getContainingNode(point);
            }
        } else {
            if (left != null && left instanceof BSPNode) {
                reply = ((BSPNode)left).getContainingNode(point);
            }
        }
        return reply;
    }

    /**
     * <code>draw</code> renders the whole bsp subtree.
     *
     * @see com.jme.scene.Spatial#draw(com.jme.renderer.Renderer)
     * @param r
     *            the renderer to draw to.
     */
    public void draw(Renderer r) {
        // draw children in back-to-front order
        Spatial left = getLeftChild();
        Spatial right = getRightChild();
       
        Camera cam = r.getCamera();
        float sgnDist = worldPlane.pseudoDistance(cam.getLocation());
        float ndD = worldPlane.getNormal().dot(cam.getDirection());
//        float cosSqr = cam.getMaxCosSqrFrustumAngle();
//       
//        // decide depending on relative position of camera to the world plane
//        if (sgnDist > 0f) {
//            if (-ndD >= cosSqr) {
//                if (right != null) {
//                    right.onDraw(r);
//                    // callback?
//                }
//            }
//            if (left != null) {
//                left.onDraw(r);
//            }
//        } else if (sgnDist < 0f) {
//            if (ndD >= cosSqr) {
//                if (left != null) {
//                    left.onDraw(r);
//                    // callback?
//                }
//                if (right != null) {
//                    right.onDraw(r);
//                }
//            }
//            if (left != null) {
//                left.onDraw(r);
//            }
//        } else if (ndD >= 0f) {
//            if (-ndD >= cosSqr) {
//                if (right != null) {
//                    right.onDraw(r);
//                    // callback?
//                }
//            }           
//            if (left != null) {
//                left.onDraw(r);
//            }
//        } else {
//            if (ndD >= cosSqr) {
//                if (left != null) {
//                    left.onDraw(r);
//                }
//                // callback?
//            }
//            if (right != null) {
//                right.onDraw(r);
//            }           
//        }
       
    }

    /**
     * <code>updateWorldData</code> updates all the children maintained by
     * this node.
     *
     * @param time
     *            the frame time.
     */
    public void updateWorldData(float time) {
        super.updateWorldData(time);
       
        // let X represent points in model space and Y = s*R*X+T represent
        // points in world space where s is the world scale, R is the world
        // rotation, and T is the world translation.  The inverse transform is
        // X = (1/s)*R^t*(Y-T).  The model plane is Dot(N0,X) = C0.  Replacing
        // the formula for X in it and applying some algebra leads to the world
        // plane Dot(N1,Y) = C1 where N1 = R*N0 and C1 = s*C0+Dot(N1,T).

        Vector3f normal = worldRotation.mult(modelPlane.getNormal());
        float constant = worldScale.length() * modelPlane.getConstant() + normal.dot(worldTranslation);
        worldPlane.setNormal(normal);
        worldPlane.setConstant(constant);
    }
   
    /**
     * Getter for the model plane.
     * @return The current model plane.
     */
    public Plane getModelPlane() {
        return modelPlane;
    }
   
    /**
     * Getter for the world plane.
     * @return The current world plane.
     */
    public Plane getWorldPlane() {
        return worldPlane;
    }
} // public class BSPNode extends Node



To use the commented out region, you need the following in AbstractCamera:


   public float getMaxCosSqrFrustumAngle() {
        // Compute (cos(A))^2 where A is the largest angle between the frustum
        // axis direction D and the four edges of the frustum that lie along the
        // rays from the frustum origin.
        float nearSqr = frustumNear * frustumNear;

        float denom = nearSqr;
        if (Math.abs(frustumLeft) >= Math.abs(frustumRight)) {
            denom += frustumLeft * frustumLeft;
            if (Math.abs(frustumBottom) >= Math.abs(frustumTop)) {
                denom += frustumBottom * frustumBottom;
            } else {
                denom += frustumTop * frustumTop;
            }
        } else {
            denom += frustumRight * frustumRight;
            if (Math.abs(frustumBottom) >= Math.abs(frustumTop)) {
                denom += frustumBottom * frustumBottom;
            } else {
                denom += frustumTop * frustumTop;
            }
        }

        return nearSqr / denom;
    }