function colPush(scene, node1ID, node2ID) {
    this.scene = scene;
    this.sceneID = scene.GetID();
    this.node1ID = node1ID;
    this.node2ID = node2ID;

    this.perform = function (evt) {
        //LOG("collision callback called");
        var cdata = evt.colReport;
        //LOG("cdata retrieved");
        if ((this.sceneID == cdata.sceneID) && (((this.node1ID == cdata.collidingMovableTreeID) && (this.node2ID == cdata.collidingBarrierTreeID)) || ((this.node1ID == cdata.collidingBarrierTreeID) && (this.node2ID == cdata.collidingMovableTreeID)))) {
            //LOG("condition satisfied");
            var vec = new Vector3(-(cdata.collisionNormal.x), -(cdata.collisionNormal.y), -(cdata.collisionNormal.z));
            this.scene.Transform(this.node2ID, Matrix.Translation(vec));
            //LOG("translation posted");
        }
    };
};

function colRot(scene, node1ID, node2ID) {
    this.scene = scene;
    this.sceneID = scene.GetID();
    this.node1ID = node1ID;
    this.node2ID = node2ID;

    this.perform = function (evt) {
        var cdata = evt.colReport;
        //LOG("cdata retrieved");
        if ((this.sceneID == cdata.sceneID) && (((this.node1ID == cdata.collidingMovableTreeID) && (this.node2ID == cdata.collidingBarrierTreeID)) || ((this.node1ID == cdata.collidingBarrierTreeID) && (this.node2ID == cdata.collidingMovableTreeID)))) {
            //LOG("condition satisfied");
            var resultVec = cdata.collisionNormal.Cross(cdata.collisionCenter);
            //LOG("cross product obtained: "+resultVec.x+";"+resultVec.y+";"+resultVec.z);
            if (resultVec.z > 0) {
                //rotate ccw around Z
                //LOG("rotating ccw");
                this.scene.Rotate(this.node2ID, -1, 0, 0, 1);
            } else {
                //rotate cw around Z
                //LOG("rotating cw");
                this.scene.Rotate(this.node2ID, 1, 0, 0, 1);
            }
            //LOG("rotation done");
        }
    };
};

//rotate with node about given point and axis, it respect maximum and minimum angle, at begin it use start angle if defined or use minimum angle
function colAxisRot(scene, movableTreeID, nodeTransCol, nodeTrans, vecPoint, vecAxis, fLow, fHigh, fStart) {
    this.scene = scene;
    this.sceneID = scene.GetID();
    this.movableTreeID = movableTreeID;
    this.nodeTransCol = nodeTransCol;
    this.nodeTrans = nodeTrans;
    tmpNode=scene.GetNode(nodeTrans);
    if (tmpNode != undefined)
        this.nodeTransParent = tmpNode.GetParentID();
    else
        this.nodeTransParent = -1;
    this.vecPoint = vecPoint;
    this.vecAxis = vecAxis.Normalize();
    this.fLow = fLow;
    this.fHigh = fHigh;
    if (fStart != undefined)
        this.currRot = fStart;
    else
        this.currRot = fLow;

    this.Rotate = function (angle) {
        if (angle != undefined)
            this.currRot = angle;
        if (this.currRot < this.fLow)
            this.currRot = this.fLow;
        if (this.currRot > this.fHigh)
            this.currRot = this.fHigh;

        //LOG("performing transformation");
        var vpinv = new Vector3(-this.vecPoint.x, -this.vecPoint.y, -this.vecPoint.z);
        var mat = Matrix.Translation(vpinv);
        //LOG("Got translation");
        mat = mat.Multiply(Matrix.RotationAxis(this.vecAxis, -(this.currRot) * 3.141592 / 180.0));
        //LOG("Got rotation");
        mat = mat.Multiply(Matrix.Translation(this.vecPoint));
        //LOG("Got untranslation");
        this.scene.SetTransformation(this.nodeTrans, mat);
        //LOG("transformed");
    }

    this.Rotate();//initialize starting position

    this.perform = function (evt) {
        var cdata = evt.colReport;
        //LOG("cdata retrieved");
        if (this.sceneID != cdata.sceneID)
        	return;
		//LOG("colTrigger cdata.sceneID " + cdata.sceneID + " cdata.collidingMovableTreeID " + cdata.collidingMovableTreeID + " cdata.collidingBarrierTreeID " + cdata.collidingBarrierTreeID +
		//	" collidingMovableGeometryID " + cdata.collidingMovableGeometryID + " collidingBarrierGeometryID " + cdata.collidingBarrierGeometryID/* + " cdata.depthOfPenetration " + cdata.depthOfPenetration
		//	 + " cdata.collisionNormal " + cdata.collisionNormal.toString()*/);
		//LOG("this.sceneID " + this.sceneID + " this.movableTreeID " + this.movableTreeID + " this.nodeTransCol " + this.nodeTransCol);
        if ((((this.movableTreeID == cdata.collidingMovableTreeID) && (this.nodeTransCol == cdata.collidingBarrierTreeID)) 
			|| ((this.movableTreeID == cdata.collidingBarrierTreeID) && (this.nodeTransCol == cdata.collidingMovableTreeID)))) {
            //LOG("condition satisfied");
            var parGlobMat = this.scene.GetWorldTransMatrix(this.nodeTransParent);
            //LOG("got matrix");
            var parMatInverse = parGlobMat.Inverse();
            //LOG("Matrix inverted");
            var transPoint = parMatInverse.TransformCoord(cdata.collisionCenter);
            //LOG("point transformed");
            var transNorm = parMatInverse.TransformNormal(cdata.collisionNormal);
            //LOG("normal transformed");
            var toPoint = this.vecPoint.Minus(transPoint);
            //LOG("vectors subtracted");
            var cross = transNorm.Cross(toPoint);
            //LOG("cross product obtained");
            var dot = cross.Dot(this.vecAxis);
            //LOG("dot product obtained");
            if (dot > 0) {
                //rotate ccw around axis
                //	LOG("rotating ccw");
                //this.currRot++;
                this.currRot += cdata.depthOfPenetration;
            } else {
                //rotate cw around axis
                //	LOG("rotating cw");
                //this.currRot--;
                this.currRot -= cdata.depthOfPenetration;
            }
            //LOG("Collision: cdata.depthOfPenetration=" + cdata.depthOfPenetration + ", has details=" + cdata.hasDetails + ", intersectionLength=" + cdata.intersectionLength);

            this.Rotate();
        }
    }
};

//rotate with node about given point and axis, it respect maximum and minimum angle, at begin it use start angle if defined or use minimum angle
function colAxisRotMulti(scene, movableTreeIDs, nodeTransCol, nodeTrans, vecPoint, vecAxis, fLow, fHigh, fStart) {
    this.scene = scene;
    this.sceneID = scene.GetID();
    this.movableTreeIDs = movableTreeIDs;
    this.nodeTransCol = nodeTransCol;
    this.nodeTrans = nodeTrans;
    tmpNode = scene.GetNode(nodeTrans);
    if (tmpNode != undefined)
        this.nodeTransParent = tmpNode.GetParentID();
    else
        this.nodeTransParent = -1;
    this.vecPoint = vecPoint;
    this.vecAxis = vecAxis.Normalize();
    this.fLow = fLow;
    this.fHigh = fHigh;
    if (fStart != undefined)
        this.currRot = fStart;
    else
        this.currRot = fLow;

    this.Rotate = function (angle) {
        if (angle != undefined)
            this.currRot = angle;
        if (this.currRot < this.fLow)
            this.currRot = this.fLow;
        if (this.currRot > this.fHigh)
            this.currRot = this.fHigh;

        //LOG("performing transformation");
        var vpinv = new Vector3(-this.vecPoint.x, -this.vecPoint.y, -this.vecPoint.z);
        var mat = Matrix.Translation(vpinv);
        //LOG("Got translation");
        mat = mat.Multiply(Matrix.RotationAxis(this.vecAxis, -(this.currRot) * 3.141592 / 180.0));
        //LOG("Got rotation");
        mat = mat.Multiply(Matrix.Translation(this.vecPoint));
        //LOG("Got untranslation");
        this.scene.SetTransformation(this.nodeTrans, mat);
        //LOG("transformed");
    }

    this.Rotate();//initialize starting position

    this.perform = function (evt) {
        var cdata = evt.colReport;
        //LOG("cdata retrieved");
        if (this.sceneID != cdata.sceneID)
            return;
        //LOG("colTrigger cdata.sceneID " + cdata.sceneID + " cdata.collidingMovableTreeID " + cdata.collidingMovableTreeID + " cdata.collidingBarrierTreeID " + cdata.collidingBarrierTreeID +
        //	" collidingMovableGeometryID " + cdata.collidingMovableGeometryID + " collidingBarrierGeometryID " + cdata.collidingBarrierGeometryID/* + " cdata.depthOfPenetration " + cdata.depthOfPenetration
        //	 + " cdata.collisionNormal " + cdata.collisionNormal.toString()*/);
        //LOG("this.sceneID " + this.sceneID + " this.movableTreeID " + this.movableTreeID + " this.nodeTransCol " + this.nodeTransCol);
        for (var i = 0; i < this.movableTreeIDs.length; ++i) {
            if ((((this.movableTreeIDs[i] == cdata.collidingMovableTreeID) && (this.nodeTransCol == cdata.collidingBarrierTreeID))
                || ((this.movableTreeIDs[i] == cdata.collidingBarrierTreeID) && (this.nodeTransCol == cdata.collidingMovableTreeID)))) {
                //LOG("condition satisfied");
                var parGlobMat = this.scene.GetWorldTransMatrix(this.nodeTransParent);
                //LOG("got matrix");
                var parMatInverse = parGlobMat.Inverse();
                //LOG("Matrix inverted");
                var transPoint = parMatInverse.TransformCoord(cdata.collisionCenter);
                //LOG("point transformed");
                var transNorm = parMatInverse.TransformNormal(cdata.collisionNormal);
                //LOG("normal transformed");
                var toPoint = this.vecPoint.Minus(transPoint);
                //LOG("vectors subtracted");
                var cross = transNorm.Cross(toPoint);
                //LOG("cross product obtained");
                var dot = cross.Dot(this.vecAxis);
                //LOG("dot product obtained");
                if (dot > 0) {
                    //rotate ccw around axis
                    //	LOG("rotating ccw");
                    //this.currRot++;
                    this.currRot += cdata.depthOfPenetration;
                } else {
                    //rotate cw around axis
                    //	LOG("rotating cw");
                    //this.currRot--;
                    this.currRot -= cdata.depthOfPenetration;
                }
                //LOG("Collision: cdata.depthOfPenetration=" + cdata.depthOfPenetration + ", has details=" + cdata.hasDetails + ", intersectionLength=" + cdata.intersectionLength);

                this.Rotate();
                break;
            }
        }
    }
};

function colTrigger(sceneID, movableTreeID, movableGeoID, barrierTreeID, barrierGeoID, triggerable) {
	this.sceneID = sceneID;
	this.movableTreeID = movableTreeID;
	this.movableGeoID = movableGeoID;
	this.barrierTreeID = barrierTreeID;
	this.barrierGeoID = barrierGeoID;
	this.trigger = triggerable;

	this.perform = function (evt) {
	    var cdata = evt.colReport;
		/*LOG("colTrigger cdata.sceneID " + cdata.sceneID + " cdata.collidingMovableTreeID " + cdata.collidingMovableTreeID + " cdata.collidingBarrierTreeID " + cdata.collidingBarrierTreeID +
			" collidingMovableGeometryID " + cdata.collidingMovableGeometryID + " collidingBarrierGeometryID " + cdata.collidingBarrierGeometryID);
		LOG("this.sceneID " + this.sceneID + " this.movableTreeID " + this.movableTreeID + " this.barrierTreeID " + this.barrierTreeID);*/
		if (this.sceneID != cdata.sceneID)
			return;
		if ((this.movableTreeID == cdata.collidingMovableTreeID) && (this.barrierTreeID == cdata.collidingBarrierTreeID)) {
			if (((this.movableGeoID == cdata.collidingMovableGeometryID) || (this.movableGeoID == -1)) && ((this.barrierGeoID == cdata.collidingBarrierGeometryID) || (this.barrierGeoID == -1))) {
				this.trigger.active = true;
			}
		}
		else if ((this.movableTreeID == cdata.collidingBarrierTreeID) && (this.barrierTreeID == cdata.collidingMovableTreeID)) {
			if (((this.movableGeoID == cdata.collidingBarrierGeometryID) || (this.movableGeoID == -1)) && ((this.barrierGeoID == cdata.collidingMovableGeometryID) || (this.barrierGeoID == -1))) {
				this.trigger.active = true;
			}
		}
	}
};

function colTriggerMulti(sceneID, movableTreeIDs, barrierTreeID, triggerable) {
    this.sceneID = sceneID;
    this.movableTreeIDs = movableTreeIDs;
    this.barrierTreeID = barrierTreeID;
    this.trigger = triggerable;

    this.perform = function (evt) {
        var cdata = evt.colReport;
        /*LOG("colTrigger cdata.sceneID " + cdata.sceneID + " cdata.collidingMovableTreeID " + cdata.collidingMovableTreeID + " cdata.collidingBarrierTreeID " + cdata.collidingBarrierTreeID +
			" collidingMovableGeometryID " + cdata.collidingMovableGeometryID + " collidingBarrierGeometryID " + cdata.collidingBarrierGeometryID);
		LOG("this.sceneID " + this.sceneID + " this.movableTreeID " + this.movableTreeID + " this.barrierTreeID " + this.barrierTreeID);*/
        if (this.sceneID != cdata.sceneID)
            return;
        for (var i = 0; i < this.movableTreeIDs.length; ++i) {
            if ((((this.movableTreeIDs[i] == cdata.collidingMovableTreeID) && (this.barrierTreeID == cdata.collidingBarrierTreeID))
                || ((this.movableTreeIDs[i] == cdata.collidingBarrierTreeID) && (this.barrierTreeID == cdata.collidingMovableTreeID)))) {
                this.trigger.active = true;
            }
        }
    }
};

//Callback used for handling EVT_SCENE_COLLISION like virtual button
function actionOnCollision(sceneID, movableTreeID, movableGeoID, barrierTreeID, barrierGeoID, action) {
    this.sceneID = sceneID;
    this.movableTreeID = movableTreeID;
    this.movableGeoID = movableGeoID;
    this.barrierTreeID = barrierTreeID;
    this.barrierGeoID = barrierGeoID;
    this.action = action;

    this.perform = function (evt) {
        var cdata = evt.colReport;
        /*LOG("colTrigger cdata.sceneID " + cdata.sceneID + " cdata.collidingMovableTreeID " + cdata.collidingMovableTreeID + " cdata.collidingBarrierTreeID " + cdata.collidingBarrierTreeID +
        " collidingMovableGeometryID " + cdata.collidingMovableGeometryID + " collidingBarrierGeometryID " + cdata.collidingBarrierGeometryID);
        LOG("this.sceneID " + this.sceneID + " this.movableTreeID " + this.movableTreeID + " this.barrierTreeID " + this.barrierTreeID);*/
        if (this.sceneID != cdata.sceneID)
            return;
        if ((this.movableTreeID == cdata.collidingMovableTreeID) && (this.barrierTreeID == cdata.collidingBarrierTreeID)) {
            if (((this.movableGeoID == cdata.collidingMovableGeometryID) || (this.movableGeoID == -1)) && ((this.barrierGeoID == cdata.collidingBarrierGeometryID) || (this.barrierGeoID == -1))) {
                this.action(evt);
            }
        }
        else if ((this.movableTreeID == cdata.collidingBarrierTreeID) && (this.barrierTreeID == cdata.collidingMovableTreeID)) {
            if (((this.movableGeoID == cdata.collidingBarrierGeometryID) || (this.movableGeoID == -1)) && ((this.barrierGeoID == cdata.collidingMovableGeometryID) || (this.barrierGeoID == -1))) {
                this.action(evt);
            }
        }
    }
}

//Callback used for handling EVT_SCENE_COLLISION like virtual button
function actionOnCollisionTimeout(sceneID, movableTreeID, movableGeoID, barrierTreeID, barrierGeoID, action) {
    this.sceneID = sceneID;
    this.movableTreeID = movableTreeID;
    this.movableGeoID = movableGeoID;
    this.barrierTreeID = barrierTreeID;
    this.barrierGeoID = barrierGeoID;
    this.action = action;
    this.lastTimerValue = timerValue;

    this.perform = function (evt) {
        if (this.lastTimerValue == timerValue)
            return;
        this.lastTimerValue = timerValue;
        var cdata = evt.colReport;
        /*LOG("colTrigger cdata.sceneID " + cdata.sceneID + " cdata.collidingMovableTreeID " + cdata.collidingMovableTreeID + " cdata.collidingBarrierTreeID " + cdata.collidingBarrierTreeID +
        " collidingMovableGeometryID " + cdata.collidingMovableGeometryID + " collidingBarrierGeometryID " + cdata.collidingBarrierGeometryID);
        LOG("this.sceneID " + this.sceneID + " this.movableTreeID " + this.movableTreeID + " this.barrierTreeID " + this.barrierTreeID);*/
        if (this.sceneID != cdata.sceneID)
            return;
        if ((this.movableTreeID == cdata.collidingMovableTreeID) && (this.barrierTreeID == cdata.collidingBarrierTreeID)) {
            if (((this.movableGeoID == cdata.collidingMovableGeometryID) || (this.movableGeoID == -1)) && ((this.barrierGeoID == cdata.collidingBarrierGeometryID) || (this.barrierGeoID == -1))) {
                this.action(evt);
            }
        }
        else if ((this.movableTreeID == cdata.collidingBarrierTreeID) && (this.barrierTreeID == cdata.collidingMovableTreeID)) {
            if (((this.movableGeoID == cdata.collidingBarrierGeometryID) || (this.movableGeoID == -1)) && ((this.barrierGeoID == cdata.collidingMovableGeometryID) || (this.barrierGeoID == -1))) {
                this.action(evt);
            }
        }
    }
}
