# /*
# -----------------------------------------------------------------------------
# This source file is part of OGRE
#     (Object-oriented Graphics Rendering Engine)
# For the latest info, see http:##www.ogre3d.org/

# Copyright (c) 2000-2006 Torus Knot Software Ltd
# Also see acknowledgements in Readme.html

# You may use self sample code for anything you like, it is not covered by the
# LGPL like the rest of the engine.
# -----------------------------------------------------------------------------
# */
# /* Static water simulation by eru
#  * Started 29.05.2003, 20:54:37
#  */

#include "ExampleApplication.h"
import OgreBillboardParticleRenderer
import WaterMesh.h
import math

AnimationState* mAnimState 

## Mesh stuff
MESH_NAME ="WaterMesh"
ENTITY_NAME ="WaterEntity"
MATERIAL_PREFIX ="Examples/Water"
MATERIAL_NAME ="Examples/Water0"
COMPLEXITY =64      ## watch out - number of polys is 2*ACCURACY*ACCURACY !
PLANE_SIZE =3000.0
CIRCLES_MATERIAL ="Examples/Water/Circles"

# /* Some global variables */
headNode  = None
waterOverlay  = None
particleSystem  = None
particleEmitter  = None
sceneMgr  = None

def prepareCircleMaterial(self):
    MESH_NAME ="WaterMesh"
    ENTITY_NAME ="WaterEntity"
    MATERIAL_PREFIX ="Examples/Water"
    MATERIAL_NAME ="Examples/Water0"
    COMPLEXITY =64      ## watch out - number of polys is 2*ACCURACY*ACCURACY !
    PLANE_SIZE =3000.0
    CIRCLES_MATERIAL ="Examples/Water/Circles"

    storageclass = ctypes.c_float * (256 * 256 * 4)
    bmap=storageclass(1.1)
    ctypes.memset ( bmap, 127, 256 * 256 * 4 )
        
    for  b in range(16):
        x0 = b % 4  
        y0 = b >> 2  
        radius = 4.0 + 1.4 * b  
        for x in range(64):
            for y in range (64) :
                dist = math.sqrt((x-32)*(x-32)+(y-32)*(y-32))  ## 0..ca.45
                dist = math.fabs(dist-radius-2) / 2.0  
                dist = dist * 255 
                if (dist>255):
                    dist=255  
                colour = 255-dist  
                colour = ((15-b))/15.0 * colour 

                bmap[4*(256*(y+64*y0)+x+64*x0)+0]=colour  
                bmap[4*(256*(y+64*y0)+x+64*x0)+1]=colour  
                bmap[4*(256*(y+64*y0)+x+64*x0)+2]=colour  
                bmap[4*(256*(y+64*y0)+x+64*x0)+3]=colour  

                
    ##  
    ms = ogre.MemoryDataStream(ctypes.addressof(bmap),  (256 * 256 * 4 ) )
    imgstream = ogre.DataStream ( ms )
    
    ##~ Image img 
    ##~ img.loadRawData( imgstream, 256, 256, PF_A8R8G8B8 ) 
    ##~ TextureManager.getSingleton().loadImage( CIRCLES_MATERIAL , img ) 
    ogre.TextureManager.getSingleton().loadRawData(CIRCLES_MATERIAL,
        ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME,
        imgstream, 256, 256, ogre.PixelFormat.PF_A8R8G8B8, 
        ogre.TextureType.TEX_TYPE_2D, -1, 1.0 ) 
        
    material = ogre.MaterialManager.getSingleton().create( CIRCLES_MATERIAL,
        ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME) 
    texLayer = material.getTechnique(0).getPass(0).createTextureUnitState( CIRCLES_MATERIAL ) 
    texLayer.setTextureAddressingMode( ogre.TextureUnitState.TAM_CLAMP ) 
    material.setSceneBlending( ogre.SceneBlendType.SBT_ADD ) 
    material.setDepthWriteEnabled( False )  
    material.load() 
    ## finished with bmap so release the memory
    del bmap 



# /* =========================================================================*/
# /*               WaterCircle class                                          */
# /* =========================================================================*/
CIRCLE_SIZE = 500.0
CIRCLE_TIME = 0.5
class WaterCircle:
    def __init_(self):
        self.name = ""
#         SceneNode *node  
#         MeshPtr mesh  
#         SubMesh *subMesh  
#         Entity *entity  
#         Real tm  
#         static bool first  
#         ## some buffers shared by all circles
#         static HardwareVertexBufferSharedPtr posnormVertexBuffer  
#         static HardwareIndexBufferSharedPtr indexBuffer   ## indices for 2 faces
#         static HardwareVertexBufferSharedPtr *texcoordsVertexBuffers  
    
#         float *texBufData 
        _prepareMesh()
    
        mesh = Ogre.MeshManager.getSingleton().createManual(name,
                Ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME)  
        subMesh = mesh.createSubMesh() 
        subMesh.useSharedVertices=False 
        numVertices = 4  
        first = True
        
        if (first) { ## first Circle, create some static common data:
            first = False  
    
            ## static buffer for position and normals
            size = 2 * ogre.VertexElement.getTypeSize(ogre.VertexElementType.VET_FLOAT3)    ## 6 * float
            posnormVertexBuffer =
                Ogre.HardwareBufferManager.getSingleton().createVertexBuffer(
                    size, ## size of one vertex data 6 * float
                    4, ## number of vertices
                    Ogre.HardwareBuffer.HBU_STATIC_WRITE_ONLY, ## usage
                    False)  ## no shadow buffer
                posnormBufData = posnormVertexBuffer.lock(Ogre.HardwareBuffer.HBL_DISCARD) 
                
                buff=[]
                for i in range(numVertices):
                    buf.append(((i%2)-0.5)*CIRCLE_SIZE ) ## pos X
                    buf.append(0)  ## pos Y
                    buf.append(((i/2)-0.5)*CIRCLE_SIZE ) ## pos Z
                    buf.append(0)   ## normal X
                    buf.append(1)   ## normal Y
                    buf.append(0)   ## normal Z
                ogre.setFloat( posnormBufData , buff )    # write unsigned ints...
                posnormVertexBuffer.unlock() 
    
                ## static buffers for 16 sets of texture coordinates
                texcoordsVertexBuffers = [] 
                for lvl in range (16):
                    texcoordsVertexBuffers.append(
                        Ogre.HardwareBufferManager.getSingleton().createVertexBuffer(
                            Ogre.VertexElement.getTypeSize(ogre.VertexElementType.VET_FLOAT2), ## size of one vertex data
                            numVertices, ## number of vertices
                            Ogre.HardwareBuffer.HBU_STATIC_WRITE_ONLY, ## usage
                            False)  ## no shadow buffer
                            )
                    texcoordsBufData = texcoordsVertexBuffers[lvl].lock(Ogre.HardwareBuffer.HBL_DISCARD) 
                    x0 = (lvl % 4) * 0.25  
                    y0 = (lvl / 4) * 0.25  
                    y0 = 0.75-y0   ## upside down
                    for i in range (4):
                        texcoordsBufData[i*2 + 0]=
                            x0 + 0.25 * (i%2)  
                        texcoordsBufData[i*2 + 1]=
                            y0 + 0.25 * (i/2)  
    
                    texcoordsVertexBuffers[lvl].unlock() 
    
    
                ## Index buffer for 2 faces
                faces[6] = {2,1,0,  2,3,1} 
                indexBuffer =
                    Ogre.HardwareBufferManager.getSingleton().createIndexBuffer(
                        Ogre.HardwareIndexBuffer.IT_16BIT,
                        6,
                        Ogre.HardwareBuffer.HBU_STATIC_WRITE_ONLY) 
                indexBuffer.writeData(0,
                    indexBuffer.getSizeInBytes(),
                    faces,
                    True)  ## true?
    
    
            ## Initialize vertex data
            subMesh.vertexData = Ogre.VertexData() 
            subMesh.vertexData.vertexStart = 0 
            subMesh.vertexData.vertexCount = 4 
            ## first, set vertex buffer bindings
            vbind = subMesh.vertexData.vertexBufferBinding  
            vbind.setBinding(0, posnormVertexBuffer) 
            vbind.setBinding(1, texcoordsVertexBuffers[0]) 
            ## now, set vertex buffer declaration
            vdecl = subMesh.vertexData.vertexDeclaration  
            vdecl.addElement(0, 0, Ogre.VET_FLOAT3, VOgre.ES_POSITION) 
            vdecl.addElement(0, 3*sizeof(float), Ogre.VET_FLOAT3, Ogre.VES_NORMAL) 
            vdecl.addElement(1, 0, Ogre.VET_FLOAT2, Ogre.VES_TEXTURE_COORDINATES) 
    
            ## Initialize index data
            subMesh.indexData.indexBuffer = indexBuffer 
            subMesh.indexData.indexStart = 0 
            subMesh.indexData.indexCount = 6 
    
            ## set mesh bounds
            circleBounds(-CIRCLE_SIZE/2.0, 0, -CIRCLE_SIZE/2.0f,
                CIRCLE_SIZE/2.0, 0, CIRCLE_SIZE/2.0f) 
            mesh._setBounds(circleBounds) 
            mesh.load() 
            mesh.touch() 

    def setTextureLevel():
        subMesh.vertexData.vertexBufferBinding.setBinding(1, texcoordsVertexBuffers[lvl]) 

    def WaterCircle( name,  x,  y):

        self.name = name  
        _prepareMesh() 
        node = sceneMgr.getRootSceneNode().createChild(name)
        node.translate(x*(PLANE_SIZE/COMPLEXITY), 10, y*(PLANE_SIZE/COMPLEXITY)) 
        entity = sceneMgr.createEntity(name, name) 
        entity.setMaterialName(CIRCLES_MATERIAL) 
        node.attachObject(entity) 
        tm = 0  
        lvl = 0  
        setTextureLevel() 

    de __del__():
        Ogre.MeshManager.getSingleton().remove(mesh.getHandle()) 
        sceneMgr.destroyEntity(entity.getName()) 
        sceneMgr.getRootSceneNode()).removeChild(node.getName()

    def animate(timeSinceLastFrame):

        lastlvl = lvl  
        tm += timeSinceLastFrame  
        lvl = (int) ( (Real)(tm)/CIRCLE_TIME * 16 ) 
        if (lvl<16 and lvl!=lastlvl) :
            setTextureLevel() 


    def clearStaticBuffers():

        posnormVertexBuffer = HardwareVertexBufferSharedPtr()  
        indexBuffer = HardwareIndexBufferSharedPtr()  
        for i in range (16):
            texcoordsVertexBuffers[i] = HardwareVertexBufferSharedPtr()  

        del texcoordsVertexBuffers 


bool WaterCircle.first = True  
HardwareVertexBufferSharedPtr WaterCircle.posnormVertexBuffer =
    HardwareVertexBufferSharedPtr()  
HardwareIndexBufferSharedPtr WaterCircle.indexBuffer =
    HardwareIndexBufferSharedPtr()  
HardwareVertexBufferSharedPtr* WaterCircle.texcoordsVertexBuffers = 0  

/* =========================================================================*/
/*               WaterListener class                                          */
/* =========================================================================*/
## Event handler
class WaterListener: public ExampleFrameListener

protected:
    WaterMesh *waterMesh  
    Entity *waterEntity  
    int materialNumber  
    bool skyBoxOn  
    Real timeoutDelay  

#define RAIN_HEIGHT_RANDOM 5
#define RAIN_HEIGHT_CONSTANT 5


    typedef std.vector<WaterCircle*> WaterCircles  
    WaterCircles circles  

    void processCircles(Real timeSinceLastFrame)

        for(unsigned int i=0 i<circles.size();i++) {
            circles[i].animate(timeSinceLastFrame) 

        bool found  
        do {
            found = False  
            for(WaterCircles.iterator it = circles.begin()  
                    it != circles.end() 
                    ++it) {
                if ((*it).lvl>=16) {:
                    delete (*it) 
                    circles.erase(it) 
                    found = True  
                    break  


        } while (found)  


    void processParticles()

        static int pindex = 0  
        ParticleIterator pit = particleSystem._getIterator()  
        while(!pit.end()) {
            Particle *particle = pit.getNext() 
            Vector3 ppos = particle.position 
            if (ppos.y<=0 and particle.timeToLive>0) { ## hits the water!:
                ## delete particle
                particle.timeToLive = 0.0 
                ## push the water
                float x = ppos.x / PLANE_SIZE * COMPLEXITY  
                float y = ppos.z / PLANE_SIZE * COMPLEXITY  
                float h = rand() % RAIN_HEIGHT_RANDOM + RAIN_HEIGHT_CONSTANT  
                if (x<1) x=1  :
                if (x>COMPLEXITY-1) x=COMPLEXITY-1 :
                if (y<1) y=1  :
                if (y>COMPLEXITY-1) y=COMPLEXITY-1 :
                waterMesh.push(x,y,-h)  
                WaterCircle *circle = new WaterCircle(
                    "Circle#"+StringConverter.toString(pindex++),
                    x, y) 
                circles.push_back(circle) 




    /** Head animation */
    Real headDepth  
    void animateHead(Real timeSinceLastFrame)

        ## sine track? :)
        static double sines[4] = {0,100,200,300} 
        static  double adds[4] = {0.3,-1.6,1.1,0.5} 
        static Vector3 oldPos = Vector3.UNIT_Z 
        for(int i=0 i<4;i++) {
            sines[i]+=adds[i]*timeSinceLastFrame 

        Real tx = ((sin(sines[0]) + sin(sines[1])) / 4 + 0.5 ) * (float)(COMPLEXITY-2) + 1  
        Real ty = ((sin(sines[2]) + sin(sines[3])) / 4 + 0.5 ) * (float)(COMPLEXITY-2) + 1  
        waterMesh.push(tx,ty, -headDepth) 
        Real step = PLANE_SIZE / COMPLEXITY  
        headNode.resetToInitialState() 
        headNode.scale(3,3,3) 
        Vector3 newPos = Vector3(step*tx, headDepth, step*ty) 
        Vector3 diffPos = newPos - oldPos  
        Quaternion headRotation = Vector3.UNIT_Z.getRotationTo(diffPos) 
        oldPos = newPos  
        headNode.translate(newPos) 
        headNode.rotate(headRotation) 


    ## GUI updaters
    void updateInfoParamC()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/Param_C") \
            .setCaption("[1/2]Ripple speed: "+StringConverter.toString(waterMesh.PARAM_C)) 

    void updateInfoParamD()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/Param_D") \
            .setCaption("[3/4]Distance: "+StringConverter.toString(waterMesh.PARAM_D)) 

    void updateInfoParamU()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/Param_U") \
            .setCaption("[5/6]Viscosity: "+StringConverter.toString(waterMesh.PARAM_U)) 

    void updateInfoParamT()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/Param_T") \
            .setCaption("[7/8]Frame time: "+StringConverter.toString(waterMesh.PARAM_T)) 

    void updateInfoNormals()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/Normals") \
            .setCaption(String("[N]Normals: ")+((waterMesh.useFakeNormals)?"fake":"real")) 

    void switchNormals()

        waterMesh.useFakeNormals = !waterMesh.useFakeNormals  
        updateInfoNormals()  

    void updateInfoHeadDepth()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/Depth") \
            .setCaption(String("[U/J]Head depth: ")+StringConverter.toString(headDepth)) 

    void updateInfoSkyBox()

        OverlayManager.getSingleton().getOverlayElement("Example/Water/SkyBox")
            .setCaption(String("[B]SkyBox: ")+String((skyBoxOn)?"On":"Off") ) 

    void updateMaterial()

        String materialName = MATERIAL_PREFIX+StringConverter.toString(materialNumber) 
        MaterialPtr material = MaterialManager.getSingleton().getByName(materialName) 
        if (material.isNull()):

            if(materialNumber):

                materialNumber = 0  
                updateMaterial() 
                return  

            else:

                OGRE_EXCEPT(Exception.ERR_INTERNAL_ERROR,
                    "Material "+materialName+"doesn't exist!",
                    "WaterListener.updateMaterial") 


        waterEntity.setMaterialName(materialName) 
        OverlayManager.getSingleton().getOverlayElement("Example/Water/Material") \
            .setCaption(String("[M]Material: ")+materialName) 


    void switchMaterial()

        materialNumber++ 
        updateMaterial() 

    void switchSkyBox()

        skyBoxOn = !skyBoxOn 
        sceneMgr.setSkyBox(skyBoxOn, "Examples/SceneSkyBox2") 
        updateInfoSkyBox() 


public:
    WaterListener(RenderWindow* win, Camera* cam,
        WaterMesh *waterMesh, Entity *waterEntity)
        : ExampleFrameListener(win, cam)

        self.waterMesh = waterMesh  
        self.waterEntity = waterEntity  
        materialNumber = 8 
        timeoutDelay = 0.0 
        headDepth = 2.0 
        skyBoxOn = False  

        updateMaterial() 
        updateInfoParamC() 
        updateInfoParamD() 
        updateInfoParamU() 
        updateInfoParamT() 
        updateInfoNormals() 
        updateInfoHeadDepth() 
        updateInfoSkyBox() 


    virtual ~WaterListener ()

        ## If when you finish the application is still raining there
        ## are water circles that are still being processed
        unsigned int activeCircles = self.circles.size () 

        ## Kill the active water circles
        for (unsigned int i = 0  i < activeCircles; i++)
            delete (self.circles[i]) 


    bool frameStarted( FrameEvent& evt)

        using namespace OIS 

        if( ExampleFrameListener.frameStarted(evt) == False ):

            ## check if we are exiting, if so, clear static HardwareBuffers to avoid segfault
            WaterCircle.clearStaticBuffers() 
            return False 


        mAnimState.addTime(evt.timeSinceLastFrame) 

        ## process keyboard events
        Real changeSpeed = evt.timeSinceLastFrame  

        ## adjust keyboard speed with SHIFT (increase) and CONTROL (decrease)
        if (mKeyboard.isKeyDown(OIS.KC_LSHIFT) or mKeyboard.isKeyDown(OIS::KC_RSHIFT)) {:
            changeSpeed *= 10.0  

        if (mKeyboard.isKeyDown(OIS.KC_LCONTROL)) { :
            changeSpeed /= 10.0  


        ## rain
        processCircles(evt.timeSinceLastFrame) 
        if (mKeyboard.isKeyDown(OIS.KC_SPACE)) {:
            particleEmitter.setEmissionRate(20.0) 
        } else {:
            particleEmitter.setEmissionRate(0.0) 

        processParticles() 

        ## adjust values (some macros for faster change
#define ADJUST_RANGE(_value,_plus,_minus,_minVal,_maxVal,_change,_macro) {\
    if (mKeyboard.isKeyDown(_plus)) \:
        { _value+=_change   if (_value>=_maxVal) _value = _maxVal ; _macro ; } ; \
    if (mKeyboard.isKeyDown(_minus)) \:
        { _value-=_change  if (_value<=_minVal) _value = _minVal ; _macro ; } ; \


        ADJUST_RANGE(headDepth, KC_U, KC_J, 0, 10, 0.5*changeSpeed, updateInfoHeadDepth())  

        ADJUST_RANGE(waterMesh.PARAM_C, KC_2, KC_1, 0, 10, 0.1*changeSpeed, updateInfoParamC())  

        ADJUST_RANGE(waterMesh.PARAM_D, KC_4, KC_3, 0.1, 10, 0.1*changeSpeed, updateInfoParamD())  

        ADJUST_RANGE(waterMesh.PARAM_U, KC_6, KC_5, -2, 10, 0.1*changeSpeed, updateInfoParamU())  

        ADJUST_RANGE(waterMesh.PARAM_T, KC_8, KC_7, 0, 10, 0.1*changeSpeed, updateInfoParamT())  

        timeoutDelay-=evt.timeSinceLastFrame  
        if (timeoutDelay<=0):
            timeoutDelay = 0 

#define SWITCH_VALUE(_key,_timeDelay, _macro) { \
        if (mKeyboard.isKeyDown(_key) and timeoutDelay==0) { \:
            timeoutDelay = _timeDelay   _macro ;} }

        SWITCH_VALUE(KC_N, 0.5, switchNormals()) 

        SWITCH_VALUE(KC_M, 0.5, switchMaterial()) 

        SWITCH_VALUE(KC_B, 0.5, switchSkyBox()) 

        animateHead(evt.timeSinceLastFrame) 

        waterMesh.updateMesh(evt.timeSinceLastFrame) 

        return True 



class WaterApplication : public ExampleApplication

public:
    WaterApplication()
        : waterMesh(0)




    ~WaterApplication() {
        delete waterMesh 


protected:
    WaterMesh *waterMesh  
    Entity *waterEntity  

## Just override the mandatory create scene method
    void createScene(void)

        sceneMgr = mSceneMgr  
        ## Set ambient light
        mSceneMgr.setAmbientLight(ColourValue(0.75, 0.75, 0.75)) 

        ## Create a light
        Light* l = mSceneMgr.createLight("MainLight") 
        ## Accept default settings: point light, white diffuse, just set position
        ## NB I could attach the light to a SceneNode if I wanted it to move automatically with
        ##  other objects, but I don't
        l.setPosition(200,300,100) 

        ## Create water mesh and entity
        waterMesh = new WaterMesh(MESH_NAME, PLANE_SIZE, COMPLEXITY) 
        waterEntity = mSceneMgr.createEntity(ENTITY_NAME,
            MESH_NAME) 
        ##~ waterEntity.setMaterialName(MATERIAL_NAME) 
        SceneNode *waterNode = mSceneMgr.getRootSceneNode().createChildSceneNode() 
        waterNode.attachObject(waterEntity) 

        ## Add a head, give it it's own node
        headNode = waterNode.createChildSceneNode() 
        Entity *ent = mSceneMgr.createEntity("head", "ogrehead.mesh") 
        headNode.attachObject(ent) 

        ## Make sure the camera track self node
        ##~ mCamera.setAutoTracking(True, headNode) 

        ## Create the camera node, set its position & attach camera
        SceneNode* camNode = mSceneMgr.getRootSceneNode().createChildSceneNode() 
        camNode.translate(0, 500, PLANE_SIZE) 
        camNode.yaw(Degree(-45)) 
        camNode.attachObject(mCamera) 

        ## Create light node
        SceneNode* lightNode = mSceneMgr.getRootSceneNode().createChildSceneNode() 
        lightNode.attachObject(l) 

        ## set up spline animation of light node
        Animation* anim = mSceneMgr.createAnimation("WaterLight", 20) 
        NodeAnimationTrack *track  
        TransformKeyFrame *key  
        ## create a random spline for light
        track = anim.createNodeTrack(0, lightNode) 
        key = track.createNodeKeyFrame(0) 
        for(int ff=1 ff<=19;ff++) {
            key = track.createNodeKeyFrame(ff) 
            Vector3 lpos (
                rand()%(int)PLANE_SIZE , ##- PLANE_SIZE/2,
                rand()%300+100,
                rand()%(int)PLANE_SIZE ##- PLANE_SIZE/2
                ) 
            key.setTranslate(lpos) 

        key = track.createNodeKeyFrame(20) 

        ## Create a new animation state to track self
        mAnimState = mSceneMgr.createAnimationState("WaterLight") 
        mAnimState.setEnabled(True) 

        ## Put in a bit of fog for the hell of it
        ##mSceneMgr.setFog(FOG_EXP, ColourValue.White, 0.0002) 

        ## show overlay
        waterOverlay = OverlayManager.getSingleton().getByName("Example/WaterOverlay") 
        waterOverlay.show() 

        ## Let there be rain
        particleSystem = mSceneMgr.createParticleSystem("rain",
            "Examples/Water/Rain") 
        particleEmitter = particleSystem.getEmitter(0) 
        SceneNode* rNode = mSceneMgr.getRootSceneNode().createChildSceneNode() 
        rNode.translate(PLANE_SIZE/2.0, 3000, PLANE_SIZE/2.0f) 
        rNode.attachObject(particleSystem) 
        ## Fast-forward the rain so it looks more natural
        particleSystem.fastForward(20) 
        ## It can't be set in .particle file, and we need it  )
        static_cast<BillboardParticleRenderer*>(particleSystem.getRenderer()).setBillboardOrigin(BBO_BOTTOM_CENTER) 

        prepareCircleMaterial() 


    ## Create new frame listener
    void createFrameListener(void)

        mFrameListener= new WaterListener(mWindow, mCamera, waterMesh, waterEntity) 
        mRoot.addFrameListener(mFrameListener) 



