ZGEBullet Collision Shape Viewer / Editor

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

Hello, it's the summer again, I'm back in the game...
And I rediscover why I completely stopped working on Omeganaut: setting the collision shapes is a hassle. We can't see the collision Bodies of ZGEBullet, so I had to do some magic with basic visible meshes and transformation scales. But it is still NOT simple.
I just spent 3 hours making this strip down example, and being reminded what a mess it is.
  • Move the mouse around to cast a ray on the model and see if it collides
  • Rotate the model with WASD/Arrows
  • Change the model with Spacebar
I don't even know where to start... As you can see by looking at the code, I have to set the shape of the Bullet Bodies on one side, and set the scale of the visible collision meshes on the other. And those numbers aren't even the same, as sometimes the shapes are made out of radius, some other times, just a height.

Here are the model examples:
  • Pyramid: as there is no pyramid collision shape, it's a bit tricky with just a cone
  • Trunk: the cylinder shape works fine
  • Building: same for the box
  • Plane: made out of compound shapes. This one is broken, just to show how COMPLICATED it is to set the thing right
I think that, back then, I set up the plane invisible shape with the visible collision meshes, then I translated the meshes transformation numbers to the position and rotation of the shapes that constitute it.


So I have a few things to discuss with you guys in order to see through this, if you want to join.

For starters, do you think some kind of visual editor would be possible to do, using what I started? Such as, moving the visible shape around would modify the Bullet Shape. I'm not sure if that is possible, that might just crash the game, as I remember ZgeBullet being a bit picky when playing with shapes that already are in memory. But I'm not sure, I need to try.


Or maybe the logic I used is overly complicated? The problem is that I will have a lot of complex shapes afterward, such as the plane. And putting them together is hell of a task.


Another thing, do I need to create several cubes shapes such as :
Shape_Building = zbtCreateBoxShape(1.0, 2.5, 2.0);
Shape_BigBuilding = zbtCreateBoxShape(4.0, 4.0, 2.0);
Shape_PetitCube = zbtCreateBoxShape(0.1, 0.1, 0.1);

Or should I use void zbtSetShapeLocalScaling(xptr shape, float x, float y, float z) {} on a Shape_BasicCube = zbtCreateBoxShape(1.0, 1.0, 1.0);
I am this close to only use simple cubes à la Minecraft for everything... :roll:

Lastly, I will try to see how zbtCreateConvexHullShape and zbtCreateTriangleMeshShape works. It might just be possible to add a simplified collision shape made directly in Blender, just like the rest. That would greatly simplify the process, and there are not that much objects running at the same time so it should be fine, if it is even possible.

Anyway, here's the thing:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FixedFrameRate="60" ScreenMode="0" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[//

if (ANDROID)
{
  if(App.GLBase==0) this.ModuleName="libGLESv1_CM.so";
  else this.ModuleName="libGLESv2.so";
}
else if (LINUX)
{
  this.ModuleName = "libGL.so";
}
else
{
  this.ModuleName = "opengl32";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[const int GL_DEPTH_BUFFER_BIT = 0x0100;

void glClear(int mode){}


/* Boolean */
const int GL_ZERO = 0;
const int GL_ONE = 1;

/* BlendingFactorDest */
//const int GL_SRC_COLOR = 0x0300;
const int GL_ONE_MINUS_SRC_COLOR = 0x0301;
const int GL_SRC_ALPHA = 0x0302;
const int GL_ONE_MINUS_SRC_ALPHA = 0x0303;
//const int GL_DST_ALPHA = 0x0304;
//const int GL_ONE_MINUS_DST_ALPHA = 0x0305;

/* BlendingFactorSrc */
//const int GL_DST_COLOR = 0x0306;
const int GL_ONE_MINUS_DST_COLOR = 0x0307;
//const int GL_SRC_ALPHA_SATURATE = 0x0308;

/* EnableCap */
const int GL_BLEND = 0x0BE2;
//const int GL_COLOR_LOGIC_OP = 0x0BF2;

/* LogicOp */
//const int GL_INVERT = 0x150A;

void glDisable(int cap){}
void glEnable(int cap){}

//void glLogicOp(int opcode){}
//void glColor3f(float red, float green, float blue){}

void glBlendFunc(int sfactor, int dfactor){}

// Pour le screenshot
void glReadPixels(int x, int y, int width, int height, int format, int type, xptr data){}

// Coloration des vaisseaux sur un Hit avec une Light
//void glLightfv(int light, int pname, xptr params){}

// VR
//void glClear(int mask){}
void glViewport(int X, int Y, int Width, int Height){}
void glGetIntegerv(int pname, xptr params){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExternalLibrary Comment="Bullet 3D physics" ModuleName="ZgeBullet_x64">
      <BeforeInitExp>
<![CDATA[//

if(ANDROID)
{
  this.ModuleName = "./libZgeBullet.so";
}
else if(LINUX)
{
  this.ModuleName = "./ZgeBullet.so";
}
else
{
  this.ModuleName = "ZgeBullet_x64";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[/*
  ZgeBullet Library, a wrapper for the Bullet Physics Library.
  http://bulletphysics.org

  Project home
  https://github.com/Rado-1/ZgeBullet

  Download Windows DLL and Android shared library from
  https://github.com/Rado-1/ZgeBullet/releases

  Copyright (c) 2012-2016 Radovan Cervenka

  Version: 2.4 (2016-09-07)
*/


// Constants

// Triangle mesh types
//const int ZBT_TRIANGLE_CONVEX_HULL_MESH = 1;
//const int ZBT_TRIANGLE_CONCAVE_STATIC_MESH = 2;
//const int ZBT_TRIANGLE_CONCAVE_DEFORMABLE_MESH = 3;

// Activation states
//const int ZBT_ACTIVE_TAG = 1;
//const int ZBT_ISLAND_SLEEPING = 2;
//const int ZBT_WANTS_DEACTIVATION = 3;
//const int ZBT_DISABLE_DEACTIVATION = 4;
//const int ZBT_DISABLE_SIMULATION = 5;

// Default values of constraint limits
//const float ZBT_DEFAULT_HINGE_SOFTNESS = 0.9;
//const float ZBT_DEFAULT_HINGE_BIAS_FACTOR = 0.3;
//const float ZBT_DEFAULT_HINGE_RELAXATION_FACTOR = 1.0;
//const float ZBT_DEFAULT_CONE_TWIST_SOFTNESS = 1.0;
//const float ZBT_DEFAULT_CONE_TWIST_BIAS_FACTOR = 0.3;
//const float ZBT_DEFAULT_CONE_TWIST_RELAXATION_FACTOR = 1.0;

// Vehicle tunning defaults
//const float ZBT_DEFAULT_VEHICLE_SUSP_STIFFNESS = 5.88;
//const float ZBT_DEFAULT_VEHICLE_SUSP_COMPRESSION = 0.83;
//const float ZBT_DEFAULT_VEHICLE_SUSP_DAMPING = 0.88;
//const float ZBT_DEFAULT_VEHICLE_SUSP_MAX_SUSP_TRAVEL_CM = 500.0;
//const float ZBT_DEFAULT_VEHICLE_SUSP_FORCE = 6000.0;
//const float ZBT_DEFAULT_VEHICLE_FRICTION_SLIP = 10.5;

// Axes
//const int ZBT_AXIS_X_LINEAR = 0;
//const int ZBT_AXIS_Y_LINEAR = 1;
//const int ZBT_AXIS_Z_LINEAR = 2;
//const int ZBT_AXIS_X_ANGULAR = 3;
//const int ZBT_AXIS_Y_ANGULAR = 4;
//const int ZBT_AXIS_Z_ANGULAR = 5;

// Collision flags
const int ZBT_CF_STATIC_OBJECT= 1;
//const int ZBT_CF_KINEMATIC_OBJECT= 2;
//const int ZBT_CF_NO_CONTACT_RESPONSE = 4;
//const int ZBT_CF_CUSTOM_MATERIAL_CALLBACK = 8;
//const int ZBT_CF_CHARACTER_OBJECT = 16;
//const int ZBT_CF_DISABLE_SPU_COLLISION_PROCESSING = 64;


// Functions


// World
xptr zbtCreateWorld() {}
void zbtDestroyWorld(xptr world) {}
void zbtSetCurrentWorld(xptr world) {}
void zbtSetWorldGravity(float x, float y, float z) {}
void zbtStepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep) {}


// Collision shapes
//xptr zbtCreateStaticPlaneShape(float normalX, float normalY, float normalZ, float planeConstant) {}
xptr zbtCreateBoxShape(float x, float y, float z) {}
xptr zbtCreateSphereShape(float radius) {}
xptr zbtCreateScalableSphereShape(float radius) {}
xptr zbtCreateConeShape(float radius, float height) {}
xptr zbtCreateCylinderShape(float radius, float height) {}
xptr zbtCreateCapsuleShape(float radius, float height) {}
xptr zbtCreateCompoundShape() {}
xptr zbtAddChildShape(xptr compoundShape, xptr childShape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtRemoveChildShape(xptr compoundShape, xptr childShape) {}
xptr zbtCreateHeightfieldTerrainShape(xptr heightfieldData, int width, int length, float minHeight, float maxHeight, int upAxis, int bFlipQuadEdges, int bDiamondSubdivision) {}
xptr zbtCreateConvexHullShape(xptr points, int numPoints) {}
xptr zbtCreateMultiSphereShape(xptr positions, xptr radii, int numSpheres) {}
xptr zbtCreateTriangleMeshShape(xptr triangles, int numTriangles, int meshType) {}
void zbtUpdateDeformableTriangleMesh(xptr triangleMeshShape) {}
void zbtSetShapeLocalScaling(xptr shape, float x, float y, float z) {}
//void zbtSetShapeMargin(xptr shape, float margin) {}
//void zbtDeleteShape(xptr shape) {}
void zbtDeleteAllShapes() {}


// Rigid bodies
xptr zbtCreateRigidBodyXYZ(float mass, xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtCreateRigidBody(float mass, xptr shape, xptr position, xptr rotation) {}
void zbtDeleteRigidBody(xptr rigidBody) {}
//void zbtSetMass(xptr rigidBody, float mass) {}
//void zbtSetDamping(xptr rigidBody, float linearDamping, float angularDamping) {}
//void zbtSetLinearFactor(xptr rigidBody, float x, float y, float z) {}
//void zbtSetAngularFactor(xptr rigidBody, float x, float y, float z) {}
//void zbtSetGravity(xptr rigidBody, float x, float y, float z) {}
//void zbtSetLinearVelocity(xptr rigidBody, float x, float y, float z) {}
//void zbtGetLinearVelocity(xptr rigidBody,	ref float outX, ref float outY, ref float outZ) {}
//void zbtSetAngularVelocity(xptr rigidBody, float x, float y, float z) {}
//void zbtGetAngularVelocity(xptr rigidBody, ref float outX, ref float outY, ref float outZ) {}
//void zbtApplyCentralImpulse(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyCentralImpulseLocal(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyImpulse(xptr rigidBody, float x, float y, float z,	float relX, float relY, float relZ) {}
//void zbtApplyTorque(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueImpulse(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueLocal(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueImpulseLocal(xptr rigidBody, float x, float y, float z) {}
void zbtSetSleepingThresholds(xptr rigidBody, float linear, float angular) {}


// Constraints and limits
//int zbtAreConnected(xptr rigidBodyA, xptr rigidBodyB) {}
//xptr zbtAddFixedConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bDisableCollision) {}
//xptr zbtAddPoint2PointConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ) {}
//xptr zbtAddPoint2PointConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, int bDisableCollision) {}
//xptr zbtAddHingeConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float axisX, float axisY, float axisZ) {}
//xptr zbtAddHingeConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, int bDisableCollision) {}
//void zbtSetHingeLimits(xptr hinge, float low, float high, float softness, float biasFactor, float relaxationFactor) {}
//void zbtEnableHingeAngularMotor(xptr hinge, int bEnableMotor, float targetVelocity, float maxMotorImpulse) {}
//xptr zbtAddConeTwistConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ) {}
//xptr zbtAddConeTwistConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bDisableCollision) {}
//void zbtSetConeTwistLimits(xptr twist, float swingSpanA, float swingSpanB, float twistSpan, float damping, float softness, float biasFactor, float relaxationFactor) {}
//void zbtEnableConeTwistMotor(xptr twist, int bEnableMotor, float maxMotorImpulse, float targetX, float targetY, float targetZ) {}
//xptr zbtAddSliderConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddSliderConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetSliderLimits(xptr slider, float linLower, float linUpper, float angLower, float angUpper) {}
//void zbtSetSliderSoftness(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtSetSliderRestitution(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtSetSliderDamping(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtEnableSliderLinearMotor(xptr slider, int bEnableMotor, float targetVelocity, float maxForce) {}
//void zbtEnableSliderAngularMotor(xptr slider, int bEnableMotor, float targetVelocity, float maxForce) {}
//xptr zbtAddGearConstraint(xptr rigidBodyA, xptr rigidBodyB, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, float ratio) {}
//void zbtSetGearConstraint(xptr gear, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, float ratio) {}
//xptr zbtAddGeneric6DofConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddGeneric6DofConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetGeneric6DofLimits(xptr dof, int axis, float lower, float upper) {}
//void zbtSetGeneric6DofLinearLimits(xptr dof, float lowerX, float lowerY, float lowerZ, float upperX, float upperY, float upperZ) {}
//void zbtSetGeneric6DofAngularLimits(xptr dof, float lowerX, float lowerY, float lowerZ, float upperX, float upperY, float upperZ) {}
//xptr zbtAddGeneric6DofSpringConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddGeneric6DofSpringConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetGeneric6DofSpring(xptr spring, int axis, int bEnableSpring, float stiffness, float damping, float equilibriumPoint) {}
//void zbtSetEnabled(xptr constraint, int bEnabled) {}
//void zbtDeleteConstraint(xptr constraint) {}


// Raycast vehicle
//void zbtSetVehicleTunning(float suspStiffness, float suspCompression, float suspDamping, float maxSuspTravelCm, float maxSuspForce, float frictionSlip) {}
//xptr zbtCreateRaycastVehicle(xptr carChassis, int rightAxis, int upAxis, int forwardAxis) {}
//int zbtAddWheel(xptr vehicle, float connectionPointX, float connectionPointY, float connectionPointZ, float directionX, float directionY, float directionZ, float wheelAxleX, float wheelAxleY, float wheelAxleZ, float wheelRadius, float suspRestLength, int bIsFrontWheel) {}
//void zbtSetWheelIsFront(xptr vehicle, int wheelId, int bIsFront) {}
//void zbtSetWheelRadius(xptr vehicle, int wheelId, float radius) {}
//void zbtSetWheelRollInfluence(xptr vehicle, int wheelId, float rollInfluence) {}
//void zbtSetWheelFrictionSlip(xptr vehicle, int wheelId, float frictionSlip) {}
//void zbtSetWheelSuspRestLength(xptr vehicle, int wheelId, float suspRestLength) {}
//void zbtSetWheelMaxSuspTravel(xptr vehicle, int wheelId, float maxSuspTravel) {}
//void zbtSetWheelSuspStiffness(xptr vehicle, int wheelId, float suspStiffness) {}
//void zbtSetWheelDampingCompression(xptr vehicle, int wheelId, float dampingCompression) {}
//void zbtSetWheelDampingRelaxation(xptr vehicle, int wheelId, float dampingRelaxation) {}
//void zbtSetWheelSteering(xptr vehicle, int wheelId, float steering) {}
//void zbtSetWheelEngineForce(xptr vehicle, int wheelId, float force) {}
//void zbtSetWheelBrake(xptr vehicle, int wheelId, float brake) {}
//void zbtResetVehicleSusp(xptr vehicle) {}
//float zbtGetVehicleCurrentSpeed(xptr vehicle) {}
//void zbtGetWheelPositionXYZ(xptr vehicle, int wheelId, ref float outX, ref float outY, ref float outZ) {}
//void zbtGetWheelPosition(xptr vehicle, int wheelId, xptr outPosition) {}
//void zbtGetWheelRotationXYZ(xptr vehicle, int wheelId, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetWheelRotation(xptr vehicle, int wheelId, xptr outRotation) {}
//void zbtGetWheelPosRotXYZ(xptr vehicle, int wheelId, ref float outX, ref float outY, ref float outZ, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetWheelPosRot(xptr vehicle, int wheelId, xptr outPosition, xptr outRotation) {}
//void zbtDeleteRaycastVehicle(xptr vehicle) {}


// Ghost object
//xptr zbtCreateGhostObject(xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
//void zbtDeleteGhostObject(xptr ghostObject) {}
//int zbtGetNumOverlappingObjects(xptr ghostObject) {}
//xptr zbtGetOverlappingObject(xptr ghostObject, int index) {}


// Kinematic character controller
//xptr zbtCreateKinematicCharacterController(xptr ghostObject, float stepHeight) {}
//void zbtDeleteKinematicCharacterController(xptr controller) {}
//void zbtSetCharacterUp(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterWalkDirection(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterVelocityForTimeInterval(xptr controller, float x, float y, float z, float timeInterval) {}
//void zbtCharacterWarp(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterFallSpeed(xptr controller, float fallSpeed) {}
//void zbtSetCharacterJumpSpeed(xptr controller, float jumpSpeed) {}
//void zbtSetCharacterMaxJumpHeight(xptr controller, float maxJumpHeight) {}
//int zbtCharacterCanJump(xptr controller) {}
//void zbtCharacterJump(xptr controller) {}
//void zbtSetCharacterGravity(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterMaxSlope(xptr controller, float slope) {}
//void zbtSetCharacterUseGhostSweepTest(xptr controller, int bUseGhostObjectSweepTest) {}
//int zbtCharacterOnGround(xptr controller) {}
//void zbtCharacterReset(xptr controller) {}
//void zbtSetCharacterUpInterpolate(xptr controller, int bInterpolate) {}


// Collision objects (in general)
//void zbtSetFriction(xptr obj, float friction) {}
//void zbtSetRollingFriction(xptr obj, float friction) {}
//void zbtSetRestitution(xptr obj, float restitution) {}
//void zbtSetHitFraction(xptr obj, float hitFraction) {}
void zbtGetPositionXYZ(xptr obj, ref float outX, ref float outY, ref float outZ) {}
//void zbtGetPosition(xptr obj, xptr outPosition) {}
void zbtSetPositionXYZ(xptr obj, float x, float y, float z) {}
void zbtSetPosition(xptr obj, xptr position) {}
//void zbtGetRotationXYZ(xptr obj, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetRotation(xptr obj, xptr outRotation) {}
//void zbtSetRotationXYZ(xptr obj, float rx, float ry, float rz) {}
//void zbtSetRotation(xptr obj, xptr rotation) {}
//void zbtGetRotationQuat(xptr obj, xptr outQuaternion) {}
//void zbtGetRotationDirection(xptr obj, xptr outDirection) {}
//void zbtSetRotationDirectionXYZ(xptr obj, float x, float y, float z) {}
//void zbtSetRotationDirection(xptr obj, xptr direction) {}
void zbtGetPosRotXYZ(xptr obj, ref float outX, ref float outY, ref float outZ, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetPosRot(xptr obj, xptr outPosition, xptr outRotation) {}
//void zbtSetPosRotXYZ(xptr obj, float x, float y, float z, float rx, float ry, float rz) {}
void zbtSetPosRot(xptr obj, xptr position, xptr rotation) {}
//void zbtGetModelMatrix(xptr obj, xptr outMatrix) {}
//void zbtGetModelMatrixInv(xptr obj, xptr outMatrix) {}
void zbtSetCollisionFlags(xptr obj, int flags) {}
//int zbtIsActive(xptr obj) {}
//void zbtActivate(xptr obj, int bForceActivation) {}
void zbtSetActivationState(xptr obj, int newState) {}
//void zbtForceActivationState(xptr obj, int newState) {}
//void zbtSetDeactivationTime(xptr obj, float time) {}
//void zbtSetUserIndex(xptr obj, int index) {}
//int zbtGetUserIndex(xptr obj) {}
void zbtSetUserModel(xptr obj, model userModel) {}
model zbtGetUserModel(xptr obj) {}


// Collision detection
//void zbtSetIgnoreCollisionCheck(xptr objA, xptr objB, int bIgnoreCollisionCheck) {}
void zbtSetCollisionFilterGroupAndMask(xptr obj, int group, int mask) {}
//int zbtStartCollisionDetection() {}
//int zbtGetNextContact(ref xptr outObjA, ref xptr outObjB, xptr outPosA, xptr outPosB, xptr outNormal) {}
//void zbtGetCollidedObjects(int contactIndex, ref xptr outObjA, ref xptr outObjB, ref float outAppliedImpulse) {}
int zbtIsColliding(xptr obj) {}
//int zbtGetNumberOfCollisions(xptr obj) {}
int zbtIsCollidedWith(xptr objA, xptr objB) {}
//float zbtGetCollisionImpulse(xptr obj) {}
xptr zbtGetMainCollidedObject(xptr obj) {}


// Raycasting
xptr zbtRayTest(float fromX, float fromY, float fromZ, float toX, float toY, float toZ) {}
xptr zbtRayTestFiltered(float fromX, float fromY, float fromZ, float toX, float toY, float toZ, int filterGroup, int filterMask) {}
void zbtGetRayTestHitPointXYZ(ref float outX, ref float outY, ref float outZ) {}
//void zbtGetRayTestHitPoint(xptr outPosition) {}
//void zbtGetRayTestHitNormalXYZ(ref float outX, ref float outY, ref float outZ) {}
//void zbtGetRayTestHitNormal(xptr outNormal) {}]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary Comment="Google Doc - DATA" HasInitializer="1">
      <Source>
<![CDATA[/*
  In the real project, I have a shitload of models.
  So everything is listed in a Google Sheet doc where I can check if they are listed in the database,
  set their names...and then export this list.
*/

const int
  M_PLANE=0,
  M_PYRAMID=1,
  M_TRUNK=2,
  M_BUILDING=3;

{
  DB_List.SizeDim1=4;
  DB_List[0]=M_PYRAMID;
  DB_List[1]=M_TRUNK;
  DB_List[2]=M_BUILDING;
  DB_List[3]=M_PLANE;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Body Shapes">
      <Source>
<![CDATA[const float

  CAM_FOV = 0.4142;     // = tan(App.FOV(45) / 360 * PI);


/*
  We need to create all the collision shapes here
*/

xptr

  World,
  Shape_Sphere,
  Shape_Pyramid,
  Shape_Trunk,
  Shape_Building,
  Shape_Plane;



vec3 BodyShape(int type)
{
  switch(type)
  {
    case M_PLANE:           return vector3(1.0, 0.8, 0.9);
    case M_PYRAMID:         return vector3(5.5, 4.0, 5.5);
    case M_TRUNK:           return vector3(0.5, 1.5, 0.5);
    case M_BUILDING:        return vector3(1.0, 2.5, 2.0);
  }
  // Or return no scale
  return vector3(1.0, 1.0, 1.0);
}


xptr CreateRigidBody(model object, int type)
{
  switch(type)
  {
    case M_PLANE:           return zbtCreateRigidBody(1, Shape_Plane, object.Position, object.Rotation);
    case M_PYRAMID:         return zbtCreateRigidBody(1, Shape_Pyramid, object.Position, object.Rotation);
    case M_TRUNK:           return zbtCreateRigidBody(1, Shape_Trunk, object.Position, object.Rotation);
    case M_BUILDING:        return zbtCreateRigidBody(1, Shape_Building, object.Position, object.Rotation);;
  }

  trace("\nThere is a problem with CreateRigidBody type=" + intToStr(type) + "\n");
  return zbtCreateRigidBody(1, Shape_Sphere, object.Position, object.Rotation);
}


void RenderMesh(int mesh)
{
  switch(mesh)
  {
    case M_PLANE:     @RenderMesh(Mesh:Mesh_Plane); return;
    case M_PYRAMID:   @RenderMesh(Mesh:Mesh_Pyramid); return;
    case M_TRUNK:     @RenderMesh(Mesh:Mesh_Trunk); return;
    case M_BUILDING:  @RenderMesh(Mesh:Mesh_Building); return;
  }
}



/*
  Set the visible collision Mesh, that has no collision at all...
*/

void UpdateCollisionMesh()
{
  if (Database_Body != null) zbtDeleteRigidBody(Database_Body);

  DatabaseTransformCollision.Scale = 1;
  DatabaseTransformCollision.Translate = 0;
  DatabaseTransformCollision.Rotate = 0;

  int CurrentView = DB_List[MenuData];
  switch (CurrentView)
  {
    case M_BUILDING:  DatabaseCollisionMesh.Mesh = Mesh_Cube; break;
    case M_PLANE:     DatabaseCollisionMesh.Mesh = Mesh_ShipCollision; break;
    case M_TRUNK:     DatabaseCollisionMesh.Mesh = Mesh_ColCylinder; break;
    case M_PYRAMID:   DatabaseCollisionMesh.Mesh = Mesh_Cone; break;
  }

  DatabaseTransformCollision.Scale = BodyShape(CurrentView);

  // Set the Body, that has the real collision, but is not visible
  Database_Body = CreateRigidBody(ModelViewer, CurrentView);
}]]>
      </Source>
    </ZLibrary>
    <ZExpression Comment="init physics">
      <Expression>
<![CDATA[// Init physical world

World = zbtCreateWorld();
zbtSetCurrentWorld(World);
zbtSetWorldGravity(0, 0, 0);


// Create collision shapes
// Details about shapes: https://stephengold.github.io/Minie/minie/minie-library-tutorials/shape.html
// or https://docs.panda3d.org/1.10/python/programming/physics/bullet/collision-shapes

//--- BASIC SHAPES -------------------------------------------------------------

// BOX: zbtCreateBoxShape(float x, float y, float z)
//  size x,y,z is from center, so the final size will be x2...
Shape_Building = zbtCreateBoxShape(1.0, 2.5, 2.0);

// CONE: zbtCreateConeShape(float radius, float height)
Shape_Pyramid = zbtCreateConeShape(2.75, 4.0);

// CYLINDER: zbtCreateCylinderShape(float radius, float height)
Shape_Trunk = zbtCreateCylinderShape(0.5, 1.5);

// SPHERE: zbtCreateSphereShape(float radius)
Shape_Sphere = zbtCreateSphereShape(1.0);


//--- COMBINED SHAPES ----------------------------------------------------------

xptr Plane_Hull_Shape = zbtCreateScalableSphereShape(1);
zbtSetShapeLocalScaling(Plane_Hull_Shape, 0.6, 0.3, 2.6);

xptr Plane_Wing_Shape = zbtCreateScalableSphereShape(1);
zbtSetShapeLocalScaling(Plane_Wing_Shape, 0.9, 0.1, 0.2);

xptr Plane_Left_Wing_Shape = zbtCreateCompoundShape();
zbtAddChildShape(Plane_Left_Wing_Shape, Plane_Wing_Shape, -1.3,-0.1,1.0, 0.1,0.6,0.03);

xptr Plane_Right_Wing_Shape = zbtCreateCompoundShape();
zbtAddChildShape(Plane_Right_Wing_Shape, Plane_Wing_Shape, 1.3,-0.1,1.0, -0.1,0.6,-0.53);

Shape_Plane = zbtCreateCompoundShape();
zbtAddChildShape(Shape_Plane, Plane_Hull_Shape, 0.0,0.05,-0.8, 0.0,0.0,0.0);
zbtAddChildShape(Shape_Plane, Plane_Left_Wing_Shape,  0.0,0.0,0.0, 0.0,0.0,0.0);
zbtAddChildShape(Shape_Plane, Plane_Right_Wing_Shape,  0.0,0.0,0.0, 0.0,0.0,0.0);]]>
      </Expression>
    </ZExpression>
    <SpawnModel Model="ModelViewer" Position="0 0 -8" SpawnStyle="1"/>
    <ZExpression>
      <Expression>
<![CDATA[MenuData = 0;

Database_Body = null;

UpdateCollisionMesh();]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression Comment="Mouse raycast">
      <Expression>
<![CDATA[Axis[0,0] = 0;
Axis[1,0] = 0;

zbtStepSimulation(App.DeltaTime, 0, 0);

// Mouse Raycast

float z = App.CameraPosition.Z - ModelViewer.Position.Z;
float x = z * App.MousePosition.X * CAM_FOV * App.ViewportWidth / App.ViewportHeight;
float y = z * App.MousePosition.Y * CAM_FOV;

Transform_MouseToRay.Translate.X = x;
Transform_MouseToRay.Translate.Y = y;


if (zbtRayTest(x, y, App.CameraPosition.Z, x, y, -1000) != null) // The body has been hit by the ray
{
  vec3 pos;
  zbtGetRayTestHitPointXYZ(pos.X, pos.Y, pos.Z); // Set collision point coordinates to pos
  Transform_MouseToRay.Translate.Z = pos.Z;
  MouseToRaySize.Scale = 0.004 * (App.CameraPosition.Z - pos.Z); // Set the size of the ball at the tip of the ray, where the collision happens
  DatabaseCollisionColor.Color = vector4(1,0,0,0.6); // Red
}
else
{
  Transform_MouseToRay.Translate.Z = -200;
  MouseToRaySize.Scale = 0;
  DatabaseCollisionColor.Color = vector4(1,1,1,0.2); // White
}]]>
      </Expression>
    </ZExpression>
    <KeyPress Comment="Left" Keys="&lt;QA">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Right" Keys="&gt;D">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Up" Keys="^ZW">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Down" Keys="_S">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Space" CharCode="32" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[MenuData += 1;
if(MenuData == DB_List.SizeDim1) MenuData = 0;

UpdateCollisionMesh();]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="Transform_MouseToRay" Translate="-28.3807 7.4556 -200">
      <Children>
        <UseMaterial Material="DatabaseRayMaterial"/>
        <RenderMesh Mesh="Mesh_DatabaseRay"/>
        <RenderTransformGroup Name="MouseToRaySize" Scale="0 0 0">
          <Children>
            <RenderMesh Mesh="Mesh_Sphere"/>
          </Children>
        </RenderTransformGroup>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Model Name="ModelViewer" Position="0 0 -8" Rotation="0.0892 0.1715 0" RotationVelocity="0.0004 0.0008 0">
      <OnSpawn>
        <ZExpression>
          <Expression>
<![CDATA[// Init ModelViewer position and rotation

CurrentModel.Position.X = 0;
CurrentModel.Position.Y = 0;
CurrentModel.Position.Z = -8;
CurrentModel.Rotation = 0;
CurrentModel.RotationVelocity = vector3(rnd(), rnd(), 0);]]>
          </Expression>
        </ZExpression>
      </OnSpawn>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Rotate the model with arrow keys or decelerate rotation if not

if(Axis[1,0] && abs(CurrentModel.RotationVelocity.X) < 0.4) CurrentModel.RotationVelocity.X -= 0.5 * Axis[1,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.X += 0 - App.DeltaTime * CurrentModel.RotationVelocity.X;

if(Axis[0,0] && abs(CurrentModel.RotationVelocity.Y) < 0.4) CurrentModel.RotationVelocity.Y += 0.5 * Axis[0,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.Y += 0 - App.DeltaTime * CurrentModel.RotationVelocity.Y;


// Set the Collision Body to the current rotation of the model

zbtSetPosRot(Database_Body, CurrentModel.Position, CurrentModel.Rotation);]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="GhostMaterial"/>
        <ZExpression Comment="Render Mesh">
          <Expression>
<![CDATA[// I do it that way, because I use the same RenderMesh function to render things within the game too

int mesh = DB_List[MenuData];
RenderMesh(mesh);]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="DatabaseTransformCollision">
          <Children>
            <RenderSetColor Name="DatabaseCollisionColor" Color="1 1 1 0.5"/>
            <RenderMesh Name="DatabaseCollisionMesh" Mesh="Mesh_ShipCollision"/>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
    <Array Name="DB_List" Type="1" SizeDim1="4"/>
    <Variable Name="MenuData" Type="1"/>
    <Variable Name="Database_Body" Type="9"/>
    <Group Comment="Materials">
      <Children>
        <Material Name="GhostMaterial" Shading="1" Color="1 1 1 0.2" SpecularColor="0 0 0 1" EmissionColor="0 0 0 1" Blend="1" ZBuffer="0"/>
        <Material Name="DatabaseRayMaterial" Shading="1" Color="1 0 0.502 1" Light="0" SpecularColor="1 1 1 1" EmissionColor="1 0.502 1 1" Shininess="1"/>
      </Children>
    </Group>
    <Group Comment="Meshes">
      <Children>
        <Mesh Name="Mesh_Trunk">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED983F6B9CCB1587C7B65204EE1606072EB752A3E54A95903E40E63B683B23175B48626AB5C242DDE20F2009D248A8122AB7573E8084AA354881B8BC315C9C2E8108FC669EF3EC78951B1CECC4E63AB02FEC683C7FCEF99DDF3967E68CDF3E49E90F8F13DF654A7FBCF4EFFEEF53CA99BF9B0BE72B6F5EF49FD6F1BCB99012FDF39594DFBC385FA9FFAE6BEA5C3E5FE93F7DF322A5CD0567F79652DA5B72D7DED2F9CADE12230777CC76978C77978C1FDC7555DBEDBBC593F395D3FBC593944EEFBBCBF395DB770777EC62B6FF9491FED3833B64DA67E5E602BB36175CE38818D27ED893DD4B2B1E2588EAF6DDCCA2D37B5AB6742FBB8E9691C51366174F44CB7A306305721A72B4801FE4C8A83BABF6AE3BBD87B79934FAD5F69729D1AA578D32ACBDA212E7ED3B98945BF1CBE4D4AE9026CEA6E57C65F1C456B4CE22A7BB94AB2933FB55726A1C8AB9AEE944AE2F9A8DF8424B417870272A2D12AD966205B2B428A48734E264C64C6809C6F4BE7C62639515A8F4E034BA02AD31F3D017EA9ACA746FC86CBAD8DBEC45A61C1A397228DBCC6EF56F76CE5776978DDBF58DF395EDB5B3BAFE2CEB97EDB5F395F58DDDE5F3959B1D7DB7D5D78360607CF590355717E264EFF34BE41C5D6B23F25FBD46D7F1EDEA21BAE4F0EA025DCF2FD1258747D7E87AF51A5DFAF7F8562FA38BF1D543D65C5DE853F63EBF44CED1B5DE417ED595F51AAD9CE853F977AFFE354EDC5B5796C8E81C195D22A3736474898CCE913B3932BAC05ECAE1DF12FECD21BF1045757D72EF54E6BEFEA9B3E8CDA13787DEC2BFC94664463CE4888702F290197DD7A88B5D9129D9B59129C57E446C8988CD11B12522B62829223647C496C8C7121994238372F05C22834A64508E0CCA91412532E8832DDA58639C1324B8929F2933C121B3334EE00A0971BE1567234A4B9C6059097142963821739C9085D30699AE71AF566BA332D5A5EDCD3A2DC26A2DAD115BE224CFA28E93BC20158D8C447EE5C8AFC2E9474BF6444667FACA61AFD22AD31D7E1C4CE07930A1DF750777154B076F8C9CDE0F26F4590916ACA64DFB9C5BF0825DB4D5A68EDCA4E584C33EDACA50C7294DCB99C2294DCB38FEA2653DA7342D72E08D16F9C4032DDAE18A16EDAC45EFE2C960D2507597830923221C4CF035385D231E2CC2F63A9BA7A8B2F807136200FC2923478429551E2A5A7810BF32DFBC40A69CA84BE4FDA7F08634FAA0441A39C2ACE3DA85047C4CDF35DA28AAAE43AF68654C6922518B08E55C8BF0267D2C755C4EE4CA59A5E9D9A60539482636D06E54C83C12C8C7F048B554749C1B813F7C01C3ADEF1A3D6524C82D9823F62B2AB538A25E574EF90CFEF58B3C8B4199EA129B16AD1E0E26BBCBE8DDEACBC3D5C560C2F93C989C4D3D7E743D98703E0F269CCF587D7CDB18E67C1E4C389F0713CE67B8652FE7F360C2F90C1B68E12E184C8E6F8D28B14D232DCB33ADF6DEEC804A1BD737C07396C1A3BDDB6BE0D95D068F31B6D59F451AE3DC4DE0D1D7ECE56E028F5184FC57AF65C6F89199E617BC2086F58DDE787B4D0C373BBD3123E2E98DCFB2A85C232AC7B7D77A63FACC5E5DF4C647D732B07AD81BBF7A2D33CF2F7BE3C6926B66BC6DAFB1727759EF30BBBD26FFC7B7BD71F7F2D5EBDE983E562B536ED37E6FCC6D8B2E3DA296A3EBDE983EF2F5AC329BAFC1DC22A137DEEA1B0F6268FE45027A9BAFD5EE4843D556B66811A7F1A35E67D5E8B8DE81B1AB0BD8A38F7CDBDD65E43BAB67C5A62FF491981979F55A2FE391A36BBD6C5F2DCA778D5CB9C6BD72A896AD3E16A9DDE8B265E466A7CD62A3AD1E77D648B00668B727F7A637A6779937A6771CB3518F95A8C74AD46325EAB112F558897AAC443D56A21E2B518F4D6FB7A8C74AD46325EAB112F558897AAC443D56A21E2B518F95A8C74AD46325EAB112F558399B5608B5D62A518F95A8C74AD463A5D5098C473D56A21E2B518F95A8C74AD46325EAB112F558D4245635DED7B4D6065BFDE1080ED1A22DBBCBC3D1CDCEFAC670D46C3FCBC3D1D9940DE5AF1E0E47E411F2AF2E86237CCD382B9F7FB09A71A4E13B2583598D373BC311598C4C75C93C9289257C91F68723E29FF1C606D8E4E4E87A385ADF403B793ADB2B93C7B768912579632F8C0D47E4200865EFF92598B7D790A6BD7A59AB1927E6679ED522597A180F4A685EC076B9527ED38E84E63B24CB9892E54D8DDA2E27722527482367D16BB4C883E3DA2E27CE1A694820BF8CBDE1E8F856261B33E09731D7E83B7DEADE692CC5DE16BD8C5B0DD6F320833065EE2F6ABC6A410643CA67D3EAB7E67AC6832913CFD47555526E3530E370C24EAB5FF6C264CAC433D5005AC09032F797157E549356C5D91AB555B3DC5FA0B2E65CDF004FE5239F4DEBCFED35462BEA4C2E83A446DC876A93713C0E1EEB52F612D5E0B15A463EF757BCE7B3F53035597B8F70FF6BAF2F29DF565AAD7C5F73BEE3D4EEDB4D5DED35874699F1CDE52B4C7EDAAB0D2DCA9767DF7DEE55A668D5EE6BAEA10289FEF285A837DB0B11C6EC379FF266943DDFAABE52E556CE7DC9CAB92FDCE65F90B7B72416F97ED4B3BE1FF5F8F42D19BE6EAF57D6E891872FDFC6A4ACC24063154E64D295F2A904ED923D2D92376358CCDAE23B5D690FDFE332D0E20A0932A0ED6296F3F606C7969629C854979CB7D734B6CB89FEBD7D57EFB8FAAE198E78E3D40CADFDDE98F7726FCCBD46CB1D4A04F7C6C43FEDDE526FCCBD463BBB97C985BA725A2959236D2EF4C6E4C2ECDE4C895BF5E1CDCB9AC5136F7F24741F6E70EE7474910BDEDD563EE802799A5638561AA7F7E86A9509BABA0E2DB36A6AF590358B27D639DADE2A347469FBB4D6AA18B8D76861837B8D160CBC338623F2D1F1E1887ADB95F4DD9B326BDC6B5F5BF69660DB35E291F9D3FBE188FECC23CA5497D8D425064E4BDE11C8673DE7A42739673B32E1C7B39AD31BF9F0C379EEF9DC759CC99ECC6A7F788B89CAFBC51B47549ECC9ED5E0E19CA4658438198E88135A2CE59C0455FF2978DABD0F9ECD05F078AF79DFA504126F70663927C1E39D850462033CDE2032E90D224BF22C4B535F044BC698F2956C1C1A6F8E28DF957ACA11B5CC56F20624D2CC0B6D375F64A0654DB0116B64C97C9119F348AFD9778D7B5D633CEB237D673C9B65722BABE6A319EA781B41CE145BF45D33F317AF4CE2A771455C99596A348A44227E4744EE4A51193F0D2D1CCA9208E54DE4293D4A8FEBAFEB1EA527E937F5F765FBFC87FFA3FA5B48B3FEE34FECD3439A7F1D7FF21FD63C94F0B86AFCD2B67C5EFFAE03C3F21790A9D5FF6EE3A37F6166E117E30FB9FA187B9FBEFE5DF7A4F69EA59FBB9F2BA267E9771FE5FF631EF9D4F57FED52FDBDAFEDFBEE591D79F68DFAF7DBEEC3CFFB8EFEC22771F5B118FBDAFDFF173FFEB9F278D72DA53F753F56CC3F7E555D0F7DF13572F921E7BF3C9F7FAA79F797EE71C8FCBEB6DF7F76FE7EEEFAB755E3DB69BEFF50477E98E7FBFCBEF88CF899DF17F3F3677EFE7CABFDAF71167D6E04FE2F67D1E745D7AF7716CD73F6BFE3F91FB5AEFB5BF7DBF4F7EEBBBAFEBB5F3D5F3ED6EFE6DFFC9B7FF36FFECDBFF937FFE6DFFC9B7FF36FFECDBFF9F74D7CFF0421898BCC]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Pyramid">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED51B191C2400C5CFCF01145B8063C43EC329E8C8836B836889CD1865D80A981223EFCF9172BE974F6CC270C0C19E739495E492B9DF405600B3DED4031B81E5A20B5A693392945DC329D4C27ECA5A775945E3A95D83BE25E39A2DD7CA306592E6B958AC89598DAD7402E6B95A895E17056D4F024FD6917FCC48D7942A43B9CCDEE02A1CF7AD0DC71749EA699D888A9DD07D2342AD95146B48A476666EB2A6745B7252BC794DCD3AED46296BF7A8A274B3D4D204716CE7174DC6BC51BB54A4C43F963AA36EDF26ACEDCF12E7AE6AC6D533E73DF60DE85EFD2B639F7E68DD866F3468A4D9FC57B0CB040C52BB2C00756BCF3CFF10A1A052C29FFA4C2AFACF0C30B7CF21FBC4274C9BF39CF63B9CFD47ADEFEFFF67B72E57D5E7A6E7B3B92D7]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Plane">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CDD4FB10DC23010BC3801092A4449C1082051D1E10118222BB081178884C400D904D2D1241D4C105150D222C8F3FE77424A6ADEF2CB3EDF9DEF570066902AAAB23C02930270B62AB71B606FE90497D5704056F3D9F79C724652E0BCF3322258E128224C46A02A45B8B8D76BE632BFF5E939A4E26983D675BDF50F6EDE87E87ED00CC141F080485AC5434E55A9BF6A95A319348F765575F37A95F8A7BD79C3A4FAFBD7337010C1F0268A1063C05B972206FE1D481005DCA0A117BD29E6F7863334D2877C7B92C118233CC86BA7CC4C3AB7DFD78D2E74A5399658C894FF501F4EDF7856]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Building">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED53BB4EC340105C830DE1D9A21408241A1054F448EE43CF1FA4A2E0135CF305B8434AFE800F081D4D6A40502252D20404029265E7C6CB3914D052E424AFC7B30FEFCDDE7544E458B08A9EC8E6A5013CB9C8B93D45AE2AD57284B77E2FE093B3FD0BD87E5FB5DBA205E371867BF6559425B014AAFD436029C4EA2FDC8099AC89AFFE21BDC82577DDD19EF771DDB16C3938855D3CAAAC757CB355D9C2FB3E38451656B765F113FBE8B6AC8E9C9CD1D21B7BF1D8D857D9C47FCB266A02C3A287F6807D62A7ED01F70B063CBCDCB57BC930DEBDAC409E5965133B2A9BD84BC045C039F9FAAEDD1B156024BDED019587E6ED812B5F96AC5C578C5988F12CC6034B8EF9EE0D31D9BD21260E0C26AA77B50C0C0B25A12DF1D53294AF7BC970221ECF3ACC8A7389918EDDCB59F00C700A3C15CEF3B43843CC79014F4E900CABF94CA9704DA56A0A51496A456D25A732F5F34F7D5CA5783BA8A4DB7877A82A19AF462F306F8A9F13DE1A9E3A4CC7E7C53BC58993E73DF29962D689CCD8A39AC86CC023C53B13789260DF3533BE216FFA69510DC3B805C0A92C54B949158DDCD418AF93DA1B799945CE99FF43C7E69BB7B891BE847F6681E5FD4AE5591BF2A42B32D435E3574347B193BA7DD439F3ADCB8332E6D52A6F583DB17E93F067F6F961FFCB64A9EA931DCE860A63EB70C6E23EF53DEC250DF1E380E7BF35C942C64F7CAFA8BF2DB77A67F57765E78FF829FE1DEB744DD73F595F5837EEC6]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_DatabaseRay">
          <Producers>
            <MeshBox/>
            <MeshTransform Scale="0.008 0.008 200" Position="0 0 200"/>
          </Producers>
        </Mesh>
      </Children>
    </Group>
    <Group Comment="Collision Meshes">
      <Children>
        <Mesh Name="Mesh_Cone">
          <Producers>
            <MeshSphere Scale="0.5 0.5 0.5" ZSamples="3" RadialSamples="18"/>
            <MeshTransform Rotation="0.25 0 0"/>
            <MeshExpression Expression="v.Y = v.Y &lt; 0.25 ? -0.5 : 0.5;"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_ShipCollision">
          <Producers>
            <MeshLoad Comment="hull" Mesh="Mesh_Sphere"/>
            <MeshTransform Scale="0.6 0.3 2.6" Position="0 0.05 -0.8"/>
            <MeshLoad Comment="left" Mesh="Mesh_Sphere"/>
            <MeshTransform Scale="0.9 0.1 0.2" Position="-1.3 -0.1 1" Rotation="0.1 0.6 0.03"/>
            <MeshCombine/>
            <MeshLoad Comment="right" Mesh="Mesh_Sphere"/>
            <MeshTransform Scale="0.9 0.1 0.2" Position="1.3 -0.1 1" Rotation="-0.1 0.6 -0.53"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Sphere">
          <Producers>
            <MeshBox Scale="1 0.5 1" XCount="18" YCount="12" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[//

        float E, A, K, X, Y, Z;

        // Convert range to radians

        E = v.Y*PI; // Elevation
        A = v.X*PI; // Azimuth

        // Convert spherical coordinates into cartesian

        K = cos(E);

        X = sin(A)*K;
        Y = sin(E);
        Z = cos(A)*K;

        // Assign coordinates

        v.X = X;
        v.Y = Y;
        v.Z = Z;

        n.X = X;
        n.Y = Y;
        n.Z = Z;]]>
              </Expression>
            </MeshExpression>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Cube">
          <Producers>
            <MeshBox/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Capsule">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED98ED4B546918879F7658778B5928E8834D6EE8088531156105C74D1ADC6485C0A52FAE51484E1FA4C43E682146EB0B95DBA64265854B4C22B699B1042D428B610349274A096AA0371C0B6CF2434BC10E06C2B067FBCD3553F30F2CBBD039E1C3CD75FF9C79CE7D859CF3ACF018737881315FE43B37A79DB6C85CF0EF9B5FE6B76D99769C2D73C1D62D55D57634E135ED4EC4986D554E249963DAEDE8F739F31BECE8AA7C48DE7A27B2663949F8A1A54E644FC9D8323BFAB8942E75ABC78994D69E5B6847EF85C86CCDB1A3A60B6E8C1339D645B770B11DFDFA7748ED2227F2EB08C9829576F46514D251E044563F2139556E477FFB13325DE644ACBF48C6EBEDA83D0F99DAE7441C87A4EF8C38A4F0AC569285A3EA420AC6F45B24A79FEAD320ADD3A9EF4D252FCC692774E1536FB51FC7D1CEA9C9DC98D7FE3B92BA536A327BDFE97EE165AFECE8A352C8B1D79A0F6BCE84A64A77E6BEE6393CA469538F0CCB0219EC40B046B2AC261CFF7605DD8CD3703C9CBB26188EFFB236DB1D4938D63C45E1F8B3EFE852E3A87C79385E5147A631371CFFF16738A6E83EC90FC76F5D81608A644F7138DE7A078229925D95E178EF14045324E3FBC3F13F66219822E93B250EC114C903D752DD331F9D926C9C4C7D6F8A608D24A6E8C2573DD77E30454DA66756FBC7113599D197BA5F78F291A604C11AEBC388A64A175F63039A3635A6C8600782359287EBECC43716DD0F4E13F91B07B6DB8965E5D9EE484E06ED44E30F10DC917CB0C94E54EC87608AE4F8063BE1FF09822992D8A10B8F95D889CA7EEC509339586127D68D60879ACC925D76A27D1C3BD4647A9BEC44DF4443B756BA988253BF19549E0CBE584F5CD7E7D3C51DABFFB6F6431777AC9B27B47FBAB8C3111CB2F6AEEE971553C5114D8624358E6AAE6A9264A87174A44F9327438D2F4C91C96B8A2537ED8460CA698B25BFDAD15F174B36D5423045D2AE892573EB219822D9B92396F435433045B2B13A965C7D1482359298A20B1FDA194BBE3889296A3293A158F2D9794C5193C96B88256707B1434DA6B925962CBA8C236A32C73B3FF299D3CA43B0C65A1CD6E7D3C51A6BF545ED872ED6D2EE86B47FBA69772982B5964BBA533835D6AC014D860C35D676F7699264A8B136D3ADC993A1C617A6C850E3C869739C17EDAC101C41A8B103A1C60B841A3B106ABC40A83102A1C645FADB53351698361CC29C21749930842EB385D065C210BA10260CA766B6106AA60AA1669EE96F4CD79A6466867E8F6A633449BF475DD5FD757E4F536D36B16BFC9EDC7A38A47387DFE36B86431AABFD9ED547E190A19D7ECF8B9370C864C8EF79761E9EFEDE06BF6776100E696EF17B8A2EC38F77AA86D09D39FD91438AC3FA5D38A4FAA23E1F0EE919D21EE0D984BAE592F6CC0AB106745FAC90DD7DBA77D6F44EBA351F5608F334ED96377F23D33B5C6779F5BF579C9A190E6CB7BCFA9BA30C35939C0C5A5EFDCD51869A793ED86479F53747196AA63ABEC1F2EA6F8E32905889E5ADEC873367D683159677DD085DA6CDBA6497E56D1FA7CBCC597B9B2C6FDF045DE6CFDAD02D4E170B4C1E0E7933A8DF62FED4644E5CD7B760819A8CFFB67685116A329B2774176BEFEA7EE9E2085E1CD17C209822597355F384E08BE4913ECD1F823592F8CA78942992A63DE40BE762ADAC26E4D3338092D4585B130CF9F40CA00C35D63C45219F9E0194A1C65AF9F2904FCF00CA5063AA3137E4D33380329027F921DFAD2B70ACB1F614877CAD77E8628DB5AB32E4EB9DA28B35D6F8FE90EF8F59BA58F39D12A1C61A194CD1851FB8A6DFC2143599C6497D0BA6A8C9AC7AAE5D61873A6D765677011F7DA9BB86E02BF948F3A1C614998711CD1382299263039A3F045324F19571275324ABAAAD809EEED4FDE031A0772B2BA0E77011DC911C5B66051E97427047F2DC422B702F04C11D4938A6B6E658013D93AB4B8DA9C2C55640CFE4CA5063AA60A515D033B93253E55640CFE1E2F8A21BAFB7027A0E17C11449DF197108D648168EA6BA29823592D34FF56910AC91A49E7AAB5D91C11DEB8D79DD055DACB1EE7DA7BBA68B2FECC02165AFAC809EC635496A3239139AE7F090A64D175370EC40B046725B9509EA6D4BDD8C531334266FBD09EABD580453240F2D35C13D25104C916CF59860696DB63B9270AC1963827A4756971A6BB58B4C50EFC8CA50E3A8A3C004F58E9C9A619909EABD581C5374A7F699A0DE8B453045B2F0AC380453240BC6D485608A64EB74EABB52045F24A92FCC695764B0C6EA38BA0BBA5863ED48EAAEE9E20E5370C8B1D79A0F76A8C9CCDCD73C478635ED6CA770EC40B0463273BEA16EC6A9DEA039C180602AFBDC03479C66D0A5C61A271B198F9C72E88E38D9209339E5D00438D9209339E5D0C438D920C369061C537439CD80608A24A719104C91E434038229929C6340F0453263505D38271B1983AAC970B29131A89A0CA71C98A226C329077632E71ECA649F6F643B85630782B5F4B98759603E7BFFE3380B8CC77CFEFEC7ADDDDAADFFDBFA53F8F77F98B35BBBB55B7F0AB5E35EEEE55EEEE55EEEE55EEEE55EEEE55EEEF52F5FFF00BDCB22D8]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Diamond">
          <Producers>
            <MeshSphere Scale="0.8 1 0.8" ZSamples="3" RadialSamples="4"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_ToriiCollision">
          <Producers>
            <MeshBox Comment="left" Scale="0.7 4.3 0.7"/>
            <MeshTransform Position="-4.7 -2.1 0"/>
            <MeshBox Comment="right" Scale="0.7 4.3 0.7"/>
            <MeshTransform Position="4.7 -2.1 0"/>
            <MeshCombine/>
            <MeshBox Comment="top" Scale="9.5 2 1.2"/>
            <MeshTransform Position="0 4.2 0"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_ColCylinder">
          <Producers>
            <MeshSphere ZSamples="6" RadialSamples="14"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[//V : current vertex
//VarP is set to the number of Z samples;
//VarQ is the number of circonferences wanted in the upper and lower faces
float VarP;
int VarQ = 1;
float Psi;

VarP = 6;//OrigSphere.ZSamples;
if (v.Y != 0 || v.X != 0) {
    Psi = atan2(v.Y,v.X);
    v.X = cos(Psi); //*pow(1-abs(v.Z),2);
    v.Y = sin(Psi);
    //Normals
    //n.X = cos(Psi);
    //n.Y = sin(Psi);

    if (VarQ == 0) {
        v.Z /= 1 - 2/(VarP-1);
        }
    else { //VarQ != 0
        v.Z /= 1 - (VarQ+1)*2/(VarP-1);
        if (abs(v.Z)>1.001) {   //this is because we are using floating point precision
                                     //it is better to avoid stuff like "Z > 1" due to truncament errors

            //remember: tallest's high: (1/(1 - (VarQ+1)*2/(VarP-1));
            v.X *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);
            v.Y *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);

            v.Z = v.Z/(abs(v.Z));

        }
    } //ELSE

    //Normals
    if (abs(v.Z) < 0.999) {
        n.Z = 0; //lateral surface
        }
    else {
        if (pow(v.X,2) + pow(v.Y,2) < 0.999) {
               n.X = 0;  //Upper surface
               n.Y = 0;
               }
        else {
                 //Edges normals, you can decide what to use.
        }
    }  // End of normals section

    //soft edges      Work In Progress
    /*
    if (abs(v.Z) > 0.7 && pow(v.X,2) + pow(v.Y,2) > 0.7) {
        v.Z *= 0.9;
        v.Y *= 0.9;
        v.X *= 0.9;
        }
    // */

} //OUTER IF]]>
              </Expression>
            </MeshExpression>
            <MeshTransform Rotation="0.25 0 0"/>
          </Producers>
        </Mesh>
      </Children>
    </Group>
    <Array Name="Axis" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
  </Content>
</ZApplication>
Last edited by Ats on Thu Jun 29, 2023 7:16 am, edited 1 time in total.
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: ZGEBullet Collision Shape Viewer

Post by Kjell »

Hi Ats,
Ats wrote: Wed Jun 28, 2023 8:48 pmAnd I rediscover why I completely stopped working on Omeganaut: setting the collision shapes is a hassle.
This all comes down to preference, but for imported meshes i'd personally define the collision shape(s) in the software you're using to create them ( Blender / Maya / ??? ) and use a custom exporter/converter.
Ats wrote: Wed Jun 28, 2023 8:48 pmFor starters, do you think some kind of visual editor would be possible to do, using what I started? Such as, moving the visible shape around would modify the Bullet Shape. I'm not sure if that is possible, that might just crash the game.
It's certainly possible, but i'd advise against that ( for setting up the collision bodies at least, as a debugger it'd still be useful ).
Ats wrote: Wed Jun 28, 2023 8:48 pmLastly, I will try to see how zbtCreateConvexHullShape and zbtCreateTriangleMeshShape works. It might just be possible to add a simplified collision shape made directly in Blender, just like the rest. That would greatly simplify the process, and there are not that much objects running at the same time so it should be fine, if it is even possible.
With a custom exporter/converter you can just as easily support native Bullet box / sphere / cylinder / capsule bodies as triangle-based shapes.

Basically how this could work is that you use a specific naming convention for your collision mesh objects in Blender ( etc. ) that defines if and what kind of collision object it represents. So if it's a sphere, the exporter/converter only uses the position and scale of the object, if it's a convex-hull it only uses triangle data .. for example.

K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

All right, thanks for the explanations Kjell.
After some thought, I'm planning to create an exporter in ruby for Sketchup, as I prefer that program to make low poly spaceships.

But first thing first, I need to choose a size of reference:
In ZGEBullet, when I create a basic cube, it goes like this: Shape_Cube = zbtCreateBoxShape(1.0, 1.0, 1.0);
But since those coordinates x,y,z are starting from the center of the cube, it gives a cube of 2x2x2.
And 2 meters cube is a weird size reference in the 3D editor...
And in the main time a MeshBox with scale 1,1,1 in ZGE gives a 2x2x2 cube too: "A mesh producer that generates a equal sided box around origin with the range -1 to 1."

But since I'm planning to do everything in Sketchup, and forget about setting numbers manually in ZGE once the exporter will work, I think I'm going with a 1 meter cube that corresponds to a zbtCreateBoxShape(0.5, 0.5, 0.5);
(Which is the opposite of what I was doing before :lol:)


That said, let's start with the simplest object, the sphere.

Here's my road map for the ruby script:
  • I need to know that the selected object is a sphere. Apparently, when I copy an object in Sketchup, it keeps its original name. And I can easily get info like coordinates and length (not scale) from it, and even give it a name to differentiate one sphere from another.
  • Then transform that to Shape_Sphere_[name /or number] = zbtCreateSphereShape([size in cm / 200]); to obtain the ZgeBullet Shape.
  • Then do the same to get the things needed to display the collision mesh at the correct size.
  • Then do the same for the coordinates, scalable spheres, which will work exactly like cubes, rotation...
Note: I don't need to export several shapes if they are the same. For example, some kind of thing made out of 3 basic cubes will use only one cube, so maybe I need to think of a way to name those shapes with their size parameters, instead of a specific name, such as Shape_Sphere_[radius] or something like that.

This is going to be fun :D
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

This was pretty straight forward. Tomorrow I'll attack cylinders, capsules and cones, then the compound shapes :wink:
.
simple_shapes.png
simple_shapes.png (27.52 KiB) Viewed 98007 times
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

This is taking shape, one might say.

The next step is to be able to export compound shapes, made out of one or several shapes.
It starts at one, because if the shape is rotated or translated, then it is set as a compound shape:

Example with just one translated box:
SHAPE_BOX_1000x400x30000 = zbtCreateBoxShape(10.0, 4.0, 30.0);
Shape_Slamer = zbtCreateCompoundShape();
zbtAddChildShape(Shape_Slamer, SHAPE_BOX_1000x400x30000 , 0,0,28, 0,0,0);

Example with a compound shape made out of three basic shapes:
SHAPE_BOX_800x100x400 = zbtCreateBoxShape(8, 1, 4);
SHAPE_SPHERE_200x400x700 = zbtCreateScalableSphereShape(1.0);
zbtSetShapeLocalScaling(SHAPE_SPHERE_200x400x700, 2.0, 4.0, 7.0);
Shape_Lotus = zbtCreateCompoundShape();
zbtAddChildShape(Shape_Lotus, SHAPE_SPHERE_200x400x700, -9,0,0, 0,0.1,0); // Left wing
zbtAddChildShape(Shape_Lotus, SHAPE_SPHERE_200x400x700, 9,0,0, 0,-0.1,0); // right wing
zbtAddChildShape(Shape_Lotus, SHAPE_BOX_800x100x400 , 0,0,0, 0,0,0.25); // box for the hull

In this second example, the same sphere shape is used twice. I need to be carefull with that and not export the same shape twice with the same name.
Compound shape are used for very specific stuff, so I can't think of a way to name them other than manually.

I have to think of a way to detect all the basic shapes that are bundled together as a group in Sketchup, so that it can be exported as a compound.
I think I'll get the coordinates of the center of the group, and check the positions and rotations of the included shapes from that point.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FixedFrameRate="60" ScreenMode="0" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[//

if (ANDROID)
{
  if(App.GLBase==0) this.ModuleName="libGLESv1_CM.so";
  else this.ModuleName="libGLESv2.so";
}
else if (LINUX)
{
  this.ModuleName = "libGL.so";
}
else
{
  this.ModuleName = "opengl32";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[const int GL_DEPTH_BUFFER_BIT = 0x0100;

void glClear(int mode){}


/* Boolean */
const int GL_ZERO = 0;
const int GL_ONE = 1;

/* BlendingFactorDest */
//const int GL_SRC_COLOR = 0x0300;
const int GL_ONE_MINUS_SRC_COLOR = 0x0301;
const int GL_SRC_ALPHA = 0x0302;
const int GL_ONE_MINUS_SRC_ALPHA = 0x0303;
//const int GL_DST_ALPHA = 0x0304;
//const int GL_ONE_MINUS_DST_ALPHA = 0x0305;

/* BlendingFactorSrc */
//const int GL_DST_COLOR = 0x0306;
const int GL_ONE_MINUS_DST_COLOR = 0x0307;
//const int GL_SRC_ALPHA_SATURATE = 0x0308;

/* EnableCap */
const int GL_BLEND = 0x0BE2;
//const int GL_COLOR_LOGIC_OP = 0x0BF2;

/* LogicOp */
//const int GL_INVERT = 0x150A;

void glDisable(int cap){}
void glEnable(int cap){}

//void glLogicOp(int opcode){}
//void glColor3f(float red, float green, float blue){}

void glBlendFunc(int sfactor, int dfactor){}

// Pour le screenshot
void glReadPixels(int x, int y, int width, int height, int format, int type, xptr data){}

// Coloration des vaisseaux sur un Hit avec une Light
//void glLightfv(int light, int pname, xptr params){}

// VR
//void glClear(int mask){}
void glViewport(int X, int Y, int Width, int Height){}
void glGetIntegerv(int pname, xptr params){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExternalLibrary Comment="Bullet 3D physics" ModuleName="ZgeBullet_x64">
      <BeforeInitExp>
<![CDATA[//

if(ANDROID)
{
  this.ModuleName = "./libZgeBullet.so";
}
else if(LINUX)
{
  this.ModuleName = "./ZgeBullet.so";
}
else
{
  this.ModuleName = "ZgeBullet_x64";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[/*
  ZgeBullet Library, a wrapper for the Bullet Physics Library.
  http://bulletphysics.org

  Project home
  https://github.com/Rado-1/ZgeBullet

  Download Windows DLL and Android shared library from
  https://github.com/Rado-1/ZgeBullet/releases

  Copyright (c) 2012-2016 Radovan Cervenka

  Version: 2.4 (2016-09-07)
*/


// Constants

// Triangle mesh types
//const int ZBT_TRIANGLE_CONVEX_HULL_MESH = 1;
//const int ZBT_TRIANGLE_CONCAVE_STATIC_MESH = 2;
//const int ZBT_TRIANGLE_CONCAVE_DEFORMABLE_MESH = 3;

// Activation states
//const int ZBT_ACTIVE_TAG = 1;
//const int ZBT_ISLAND_SLEEPING = 2;
//const int ZBT_WANTS_DEACTIVATION = 3;
//const int ZBT_DISABLE_DEACTIVATION = 4;
//const int ZBT_DISABLE_SIMULATION = 5;

// Default values of constraint limits
//const float ZBT_DEFAULT_HINGE_SOFTNESS = 0.9;
//const float ZBT_DEFAULT_HINGE_BIAS_FACTOR = 0.3;
//const float ZBT_DEFAULT_HINGE_RELAXATION_FACTOR = 1.0;
//const float ZBT_DEFAULT_CONE_TWIST_SOFTNESS = 1.0;
//const float ZBT_DEFAULT_CONE_TWIST_BIAS_FACTOR = 0.3;
//const float ZBT_DEFAULT_CONE_TWIST_RELAXATION_FACTOR = 1.0;

// Vehicle tunning defaults
//const float ZBT_DEFAULT_VEHICLE_SUSP_STIFFNESS = 5.88;
//const float ZBT_DEFAULT_VEHICLE_SUSP_COMPRESSION = 0.83;
//const float ZBT_DEFAULT_VEHICLE_SUSP_DAMPING = 0.88;
//const float ZBT_DEFAULT_VEHICLE_SUSP_MAX_SUSP_TRAVEL_CM = 500.0;
//const float ZBT_DEFAULT_VEHICLE_SUSP_FORCE = 6000.0;
//const float ZBT_DEFAULT_VEHICLE_FRICTION_SLIP = 10.5;

// Axes
//const int ZBT_AXIS_X_LINEAR = 0;
//const int ZBT_AXIS_Y_LINEAR = 1;
//const int ZBT_AXIS_Z_LINEAR = 2;
//const int ZBT_AXIS_X_ANGULAR = 3;
//const int ZBT_AXIS_Y_ANGULAR = 4;
//const int ZBT_AXIS_Z_ANGULAR = 5;

// Collision flags
const int ZBT_CF_STATIC_OBJECT= 1;
//const int ZBT_CF_KINEMATIC_OBJECT= 2;
//const int ZBT_CF_NO_CONTACT_RESPONSE = 4;
//const int ZBT_CF_CUSTOM_MATERIAL_CALLBACK = 8;
//const int ZBT_CF_CHARACTER_OBJECT = 16;
//const int ZBT_CF_DISABLE_SPU_COLLISION_PROCESSING = 64;


// Functions


// World
xptr zbtCreateWorld() {}
void zbtDestroyWorld(xptr world) {}
void zbtSetCurrentWorld(xptr world) {}
void zbtSetWorldGravity(float x, float y, float z) {}
void zbtStepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep) {}


// Collision shapes
//xptr zbtCreateStaticPlaneShape(float normalX, float normalY, float normalZ, float planeConstant) {}
xptr zbtCreateBoxShape(float x, float y, float z) {}
xptr zbtCreateSphereShape(float radius) {}
xptr zbtCreateScalableSphereShape(float radius) {}
xptr zbtCreateConeShape(float radius, float height) {}
xptr zbtCreateCylinderShape(float radius, float height) {}
xptr zbtCreateCapsuleShape(float radius, float height) {}
xptr zbtCreateCompoundShape() {}
xptr zbtAddChildShape(xptr compoundShape, xptr childShape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtRemoveChildShape(xptr compoundShape, xptr childShape) {}
xptr zbtCreateHeightfieldTerrainShape(xptr heightfieldData, int width, int length, float minHeight, float maxHeight, int upAxis, int bFlipQuadEdges, int bDiamondSubdivision) {}
xptr zbtCreateConvexHullShape(xptr points, int numPoints) {}
xptr zbtCreateMultiSphereShape(xptr positions, xptr radii, int numSpheres) {}
xptr zbtCreateTriangleMeshShape(xptr triangles, int numTriangles, int meshType) {}
void zbtUpdateDeformableTriangleMesh(xptr triangleMeshShape) {}
void zbtSetShapeLocalScaling(xptr shape, float x, float y, float z) {}
//void zbtSetShapeMargin(xptr shape, float margin) {}
//void zbtDeleteShape(xptr shape) {}
void zbtDeleteAllShapes() {}


// Rigid bodies
xptr zbtCreateRigidBodyXYZ(float mass, xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtCreateRigidBody(float mass, xptr shape, xptr position, xptr rotation) {}
void zbtDeleteRigidBody(xptr rigidBody) {}
//void zbtSetMass(xptr rigidBody, float mass) {}
//void zbtSetDamping(xptr rigidBody, float linearDamping, float angularDamping) {}
//void zbtSetLinearFactor(xptr rigidBody, float x, float y, float z) {}
//void zbtSetAngularFactor(xptr rigidBody, float x, float y, float z) {}
//void zbtSetGravity(xptr rigidBody, float x, float y, float z) {}
//void zbtSetLinearVelocity(xptr rigidBody, float x, float y, float z) {}
//void zbtGetLinearVelocity(xptr rigidBody,	ref float outX, ref float outY, ref float outZ) {}
//void zbtSetAngularVelocity(xptr rigidBody, float x, float y, float z) {}
//void zbtGetAngularVelocity(xptr rigidBody, ref float outX, ref float outY, ref float outZ) {}
//void zbtApplyCentralImpulse(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyCentralImpulseLocal(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyImpulse(xptr rigidBody, float x, float y, float z,	float relX, float relY, float relZ) {}
//void zbtApplyTorque(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueImpulse(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueLocal(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueImpulseLocal(xptr rigidBody, float x, float y, float z) {}
void zbtSetSleepingThresholds(xptr rigidBody, float linear, float angular) {}


// Constraints and limits
//int zbtAreConnected(xptr rigidBodyA, xptr rigidBodyB) {}
//xptr zbtAddFixedConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bDisableCollision) {}
//xptr zbtAddPoint2PointConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ) {}
//xptr zbtAddPoint2PointConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, int bDisableCollision) {}
//xptr zbtAddHingeConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float axisX, float axisY, float axisZ) {}
//xptr zbtAddHingeConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, int bDisableCollision) {}
//void zbtSetHingeLimits(xptr hinge, float low, float high, float softness, float biasFactor, float relaxationFactor) {}
//void zbtEnableHingeAngularMotor(xptr hinge, int bEnableMotor, float targetVelocity, float maxMotorImpulse) {}
//xptr zbtAddConeTwistConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ) {}
//xptr zbtAddConeTwistConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bDisableCollision) {}
//void zbtSetConeTwistLimits(xptr twist, float swingSpanA, float swingSpanB, float twistSpan, float damping, float softness, float biasFactor, float relaxationFactor) {}
//void zbtEnableConeTwistMotor(xptr twist, int bEnableMotor, float maxMotorImpulse, float targetX, float targetY, float targetZ) {}
//xptr zbtAddSliderConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddSliderConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetSliderLimits(xptr slider, float linLower, float linUpper, float angLower, float angUpper) {}
//void zbtSetSliderSoftness(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtSetSliderRestitution(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtSetSliderDamping(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtEnableSliderLinearMotor(xptr slider, int bEnableMotor, float targetVelocity, float maxForce) {}
//void zbtEnableSliderAngularMotor(xptr slider, int bEnableMotor, float targetVelocity, float maxForce) {}
//xptr zbtAddGearConstraint(xptr rigidBodyA, xptr rigidBodyB, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, float ratio) {}
//void zbtSetGearConstraint(xptr gear, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, float ratio) {}
//xptr zbtAddGeneric6DofConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddGeneric6DofConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetGeneric6DofLimits(xptr dof, int axis, float lower, float upper) {}
//void zbtSetGeneric6DofLinearLimits(xptr dof, float lowerX, float lowerY, float lowerZ, float upperX, float upperY, float upperZ) {}
//void zbtSetGeneric6DofAngularLimits(xptr dof, float lowerX, float lowerY, float lowerZ, float upperX, float upperY, float upperZ) {}
//xptr zbtAddGeneric6DofSpringConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddGeneric6DofSpringConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetGeneric6DofSpring(xptr spring, int axis, int bEnableSpring, float stiffness, float damping, float equilibriumPoint) {}
//void zbtSetEnabled(xptr constraint, int bEnabled) {}
//void zbtDeleteConstraint(xptr constraint) {}


// Raycast vehicle
//void zbtSetVehicleTunning(float suspStiffness, float suspCompression, float suspDamping, float maxSuspTravelCm, float maxSuspForce, float frictionSlip) {}
//xptr zbtCreateRaycastVehicle(xptr carChassis, int rightAxis, int upAxis, int forwardAxis) {}
//int zbtAddWheel(xptr vehicle, float connectionPointX, float connectionPointY, float connectionPointZ, float directionX, float directionY, float directionZ, float wheelAxleX, float wheelAxleY, float wheelAxleZ, float wheelRadius, float suspRestLength, int bIsFrontWheel) {}
//void zbtSetWheelIsFront(xptr vehicle, int wheelId, int bIsFront) {}
//void zbtSetWheelRadius(xptr vehicle, int wheelId, float radius) {}
//void zbtSetWheelRollInfluence(xptr vehicle, int wheelId, float rollInfluence) {}
//void zbtSetWheelFrictionSlip(xptr vehicle, int wheelId, float frictionSlip) {}
//void zbtSetWheelSuspRestLength(xptr vehicle, int wheelId, float suspRestLength) {}
//void zbtSetWheelMaxSuspTravel(xptr vehicle, int wheelId, float maxSuspTravel) {}
//void zbtSetWheelSuspStiffness(xptr vehicle, int wheelId, float suspStiffness) {}
//void zbtSetWheelDampingCompression(xptr vehicle, int wheelId, float dampingCompression) {}
//void zbtSetWheelDampingRelaxation(xptr vehicle, int wheelId, float dampingRelaxation) {}
//void zbtSetWheelSteering(xptr vehicle, int wheelId, float steering) {}
//void zbtSetWheelEngineForce(xptr vehicle, int wheelId, float force) {}
//void zbtSetWheelBrake(xptr vehicle, int wheelId, float brake) {}
//void zbtResetVehicleSusp(xptr vehicle) {}
//float zbtGetVehicleCurrentSpeed(xptr vehicle) {}
//void zbtGetWheelPositionXYZ(xptr vehicle, int wheelId, ref float outX, ref float outY, ref float outZ) {}
//void zbtGetWheelPosition(xptr vehicle, int wheelId, xptr outPosition) {}
//void zbtGetWheelRotationXYZ(xptr vehicle, int wheelId, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetWheelRotation(xptr vehicle, int wheelId, xptr outRotation) {}
//void zbtGetWheelPosRotXYZ(xptr vehicle, int wheelId, ref float outX, ref float outY, ref float outZ, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetWheelPosRot(xptr vehicle, int wheelId, xptr outPosition, xptr outRotation) {}
//void zbtDeleteRaycastVehicle(xptr vehicle) {}


// Ghost object
//xptr zbtCreateGhostObject(xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
//void zbtDeleteGhostObject(xptr ghostObject) {}
//int zbtGetNumOverlappingObjects(xptr ghostObject) {}
//xptr zbtGetOverlappingObject(xptr ghostObject, int index) {}


// Kinematic character controller
//xptr zbtCreateKinematicCharacterController(xptr ghostObject, float stepHeight) {}
//void zbtDeleteKinematicCharacterController(xptr controller) {}
//void zbtSetCharacterUp(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterWalkDirection(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterVelocityForTimeInterval(xptr controller, float x, float y, float z, float timeInterval) {}
//void zbtCharacterWarp(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterFallSpeed(xptr controller, float fallSpeed) {}
//void zbtSetCharacterJumpSpeed(xptr controller, float jumpSpeed) {}
//void zbtSetCharacterMaxJumpHeight(xptr controller, float maxJumpHeight) {}
//int zbtCharacterCanJump(xptr controller) {}
//void zbtCharacterJump(xptr controller) {}
//void zbtSetCharacterGravity(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterMaxSlope(xptr controller, float slope) {}
//void zbtSetCharacterUseGhostSweepTest(xptr controller, int bUseGhostObjectSweepTest) {}
//int zbtCharacterOnGround(xptr controller) {}
//void zbtCharacterReset(xptr controller) {}
//void zbtSetCharacterUpInterpolate(xptr controller, int bInterpolate) {}


// Collision objects (in general)
//void zbtSetFriction(xptr obj, float friction) {}
//void zbtSetRollingFriction(xptr obj, float friction) {}
//void zbtSetRestitution(xptr obj, float restitution) {}
//void zbtSetHitFraction(xptr obj, float hitFraction) {}
void zbtGetPositionXYZ(xptr obj, ref float outX, ref float outY, ref float outZ) {}
//void zbtGetPosition(xptr obj, xptr outPosition) {}
void zbtSetPositionXYZ(xptr obj, float x, float y, float z) {}
void zbtSetPosition(xptr obj, xptr position) {}
//void zbtGetRotationXYZ(xptr obj, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetRotation(xptr obj, xptr outRotation) {}
//void zbtSetRotationXYZ(xptr obj, float rx, float ry, float rz) {}
//void zbtSetRotation(xptr obj, xptr rotation) {}
//void zbtGetRotationQuat(xptr obj, xptr outQuaternion) {}
//void zbtGetRotationDirection(xptr obj, xptr outDirection) {}
//void zbtSetRotationDirectionXYZ(xptr obj, float x, float y, float z) {}
//void zbtSetRotationDirection(xptr obj, xptr direction) {}
void zbtGetPosRotXYZ(xptr obj, ref float outX, ref float outY, ref float outZ, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetPosRot(xptr obj, xptr outPosition, xptr outRotation) {}
//void zbtSetPosRotXYZ(xptr obj, float x, float y, float z, float rx, float ry, float rz) {}
void zbtSetPosRot(xptr obj, xptr position, xptr rotation) {}
//void zbtGetModelMatrix(xptr obj, xptr outMatrix) {}
//void zbtGetModelMatrixInv(xptr obj, xptr outMatrix) {}
void zbtSetCollisionFlags(xptr obj, int flags) {}
//int zbtIsActive(xptr obj) {}
//void zbtActivate(xptr obj, int bForceActivation) {}
void zbtSetActivationState(xptr obj, int newState) {}
//void zbtForceActivationState(xptr obj, int newState) {}
//void zbtSetDeactivationTime(xptr obj, float time) {}
//void zbtSetUserIndex(xptr obj, int index) {}
//int zbtGetUserIndex(xptr obj) {}
void zbtSetUserModel(xptr obj, model userModel) {}
model zbtGetUserModel(xptr obj) {}


// Collision detection
//void zbtSetIgnoreCollisionCheck(xptr objA, xptr objB, int bIgnoreCollisionCheck) {}
void zbtSetCollisionFilterGroupAndMask(xptr obj, int group, int mask) {}
//int zbtStartCollisionDetection() {}
//int zbtGetNextContact(ref xptr outObjA, ref xptr outObjB, xptr outPosA, xptr outPosB, xptr outNormal) {}
//void zbtGetCollidedObjects(int contactIndex, ref xptr outObjA, ref xptr outObjB, ref float outAppliedImpulse) {}
int zbtIsColliding(xptr obj) {}
//int zbtGetNumberOfCollisions(xptr obj) {}
int zbtIsCollidedWith(xptr objA, xptr objB) {}
//float zbtGetCollisionImpulse(xptr obj) {}
xptr zbtGetMainCollidedObject(xptr obj) {}


// Raycasting
xptr zbtRayTest(float fromX, float fromY, float fromZ, float toX, float toY, float toZ) {}
xptr zbtRayTestFiltered(float fromX, float fromY, float fromZ, float toX, float toY, float toZ, int filterGroup, int filterMask) {}
void zbtGetRayTestHitPointXYZ(ref float outX, ref float outY, ref float outZ) {}
//void zbtGetRayTestHitPoint(xptr outPosition) {}
//void zbtGetRayTestHitNormalXYZ(ref float outX, ref float outY, ref float outZ) {}
//void zbtGetRayTestHitNormal(xptr outNormal) {}]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary Comment="Google Doc - DATA" HasInitializer="1">
      <Source>
<![CDATA[/*
  In the real project, I have a shitload of models.
  So everything is listed in a Google Sheet doc where I can check if they are listed in the database,
  set their names...and then export this list.
*/

const int
  M_PLANE=0,
  M_PYRAMID=1,
  M_TRUNK=2,
  M_BUILDING=3,
  M_ROBOT=4;

{
  DB_List.SizeDim1=5;
  DB_List[0]=M_PYRAMID;
  DB_List[1]=M_TRUNK;
  DB_List[2]=M_BUILDING;
  DB_List[3]=M_PLANE;
  DB_List[4]=M_ROBOT;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Body Shapes">
      <Source>
<![CDATA[const float

  CAM_FOV = 0.4142;     // = tan(App.FOV(45) / 360 * PI);


/*
  We need to create all the collision shapes here
*/

xptr

  World,

  SHAPE_BOX_2000x5000x4000,
  SHAPE_CONE_6000x4000,
  SHAPE_SPHERE_1000,
  SHAPE_SPHERE_3230x920x4730,
  SHAPE_CAPSULE_3000x2530,
  SHAPE_CYLINDER_1000x3000;



vec3 BodyShape(int type)
{
  switch(type)
  {
    case M_PLANE:     return vector3(3.23, 0.92, 4.73);
    case M_PYRAMID:   return vector3(6.0, 4.0, 6.0);
    case M_TRUNK:     return vector3(1.0, 3.0, 1.0);
    case M_BUILDING:  return vector3(2.0, 5.0, 4.0);
    case M_ROBOT:     return vector3(3.0, 2.53, 3.0);
  }
  // Or return no scale
  return vector3(1.0, 1.0, 1.0);
}


xptr CreateRigidBody(model object, int type)
{
  switch(type)
  {
    case M_PLANE:     return zbtCreateRigidBody(1, SHAPE_SPHERE_3230x920x4730, object.Position, object.Rotation);
    case M_PYRAMID:   return zbtCreateRigidBody(1, SHAPE_CONE_6000x4000, object.Position, object.Rotation);
    case M_TRUNK:     return zbtCreateRigidBody(1, SHAPE_CYLINDER_1000x3000, object.Position, object.Rotation);
    case M_BUILDING:  return zbtCreateRigidBody(1, SHAPE_BOX_2000x5000x4000, object.Position, object.Rotation);
    case M_ROBOT:     return zbtCreateRigidBody(1, SHAPE_CAPSULE_3000x2530, object.Position, object.Rotation);
  }

  // Got to return something, otherwise ZGEBullet will crash
  trace("\nThere is a problem with CreateRigidBody type=" + intToStr(type) + "\n");
  return zbtCreateRigidBody(1, SHAPE_SPHERE_1000, object.Position, object.Rotation);
}


void RenderMesh(int mesh)
{
  switch(mesh)
  {
    case M_PLANE:     @RenderMesh(Mesh:Mesh_Plane); return;
    case M_PYRAMID:   @RenderMesh(Mesh:Mesh_Pyramid); return;
    case M_TRUNK:     @RenderMesh(Mesh:Mesh_Trunk); return;
    case M_BUILDING:  @RenderMesh(Mesh:Mesh_Building); return;
    case M_ROBOT:     @RenderMesh(Mesh:Mesh_Robot); return;
  }
}



/*
  Set the visible collision Mesh, that has no collision at all...
*/

void UpdateCollisionMesh()
{
  if (Database_Body != null) zbtDeleteRigidBody(Database_Body);

  DatabaseTransformCollision.Scale = 1;
  DatabaseTransformCollision.Translate = 0;
  DatabaseTransformCollision.Rotate = 0;

  int CurrentView = DB_List[MenuData];
  switch (CurrentView)
  {
    case M_BUILDING:  DatabaseCollisionMesh.Mesh = Mesh_Col_Box; break;
    case M_PLANE:     DatabaseCollisionMesh.Mesh = Mesh_Col_Sphere; break;
    case M_TRUNK:     DatabaseCollisionMesh.Mesh = Mesh_Col_Cylinder; break;
    case M_PYRAMID:   DatabaseCollisionMesh.Mesh = Mesh_Col_Cone; break;
    case M_ROBOT:     DatabaseCollisionMesh.Mesh = Mesh_Col_Capsule; break;
  }

  DatabaseTransformCollision.Scale = BodyShape(CurrentView);

  // Set the Body, that has the real collision, but is not visible
  Database_Body = CreateRigidBody(ModelViewer, CurrentView);
}]]>
      </Source>
    </ZLibrary>
    <ZExpression Comment="init physics">
      <Expression>
<![CDATA[// Init physical world

World = zbtCreateWorld();
zbtSetCurrentWorld(World);
zbtSetWorldGravity(0, 0, 0);


// Create collision shapes
// Details about shapes: https://stephengold.github.io/Minie/minie/minie-library-tutorials/shape.html
// or https://docs.panda3d.org/1.10/python/programming/physics/bullet/collision-shapes

//--- BASIC SHAPES -------------------------------------------------------------

SHAPE_BOX_2000x5000x4000 = zbtCreateBoxShape(1.0, 2.5, 2.0);

SHAPE_CONE_6000x4000 = zbtCreateConeShape(3.0, 4.0);

SHAPE_CYLINDER_1000x3000 = zbtCreateCylinderShape(0.5, 1.5);

SHAPE_CAPSULE_3000x2530 = zbtCreateCapsuleShape(1.5, 2.53);

SHAPE_SPHERE_1000 = zbtCreateSphereShape(0.5);

SHAPE_SPHERE_3230x920x4730 = zbtCreateScalableSphereShape(1.0);
zbtSetShapeLocalScaling(SHAPE_SPHERE_3230x920x4730, 1.615, 0.46, 2.365);





//--- COMBINED SHAPES ----------------------------------------------------------
/*
xptr Plane_Hull_Shape = zbtCreateScalableSphereShape(1);
zbtSetShapeLocalScaling(Plane_Hull_Shape, 0.6, 0.3, 2.6);

xptr Plane_Wing_Shape = zbtCreateScalableSphereShape(1);
zbtSetShapeLocalScaling(Plane_Wing_Shape, 0.9, 0.1, 0.2);

xptr Plane_Left_Wing_Shape = zbtCreateCompoundShape();
zbtAddChildShape(Plane_Left_Wing_Shape, Plane_Wing_Shape, -1.3,-0.1,1.0, 0.1,0.6,0.03);

xptr Plane_Right_Wing_Shape = zbtCreateCompoundShape();
zbtAddChildShape(Plane_Right_Wing_Shape, Plane_Wing_Shape, 1.3,-0.1,1.0, -0.1,0.6,-0.53);

Shape_Plane = zbtCreateCompoundShape();
zbtAddChildShape(Shape_Plane, Plane_Hull_Shape, 0.0,0.05,-0.8, 0.0,0.0,0.0);
zbtAddChildShape(Shape_Plane, Plane_Left_Wing_Shape,  0.0,0.0,0.0, 0.0,0.0,0.0);
zbtAddChildShape(Shape_Plane, Plane_Right_Wing_Shape,  0.0,0.0,0.0, 0.0,0.0,0.0);
*/]]>
      </Expression>
    </ZExpression>
    <SpawnModel Model="ModelViewer" Position="0 0 -6" SpawnStyle="1"/>
    <ZExpression>
      <Expression>
<![CDATA[MenuData = 0;

Database_Body = null;

UpdateCollisionMesh();]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression Comment="Mouse raycast">
      <Expression>
<![CDATA[Axis[0,0] = 0;
Axis[1,0] = 0;

zbtStepSimulation(App.DeltaTime, 0, 0);

// Mouse Raycast

float z = App.CameraPosition.Z - ModelViewer.Position.Z;
float x = z * App.MousePosition.X * CAM_FOV * App.ViewportWidth / App.ViewportHeight;
float y = z * App.MousePosition.Y * CAM_FOV;

Transform_MouseToRay.Translate.X = x;
Transform_MouseToRay.Translate.Y = y;


if (zbtRayTest(x, y, App.CameraPosition.Z, x, y, -1000) != null) // The body has been hit by the ray
{
  vec3 pos;
  zbtGetRayTestHitPointXYZ(pos.X, pos.Y, pos.Z); // Set collision point coordinates to pos
  Transform_MouseToRay.Translate.Z = pos.Z;
  MouseToRaySize.Scale = 0.006 * (App.CameraPosition.Z - pos.Z); // Set the size of the ball at the tip of the ray, where the collision happens
  DatabaseCollisionColor.Color = vector4(1,0,0,0.6); // Red
  MouseToRayColor.Color = vector4(1,0,0,1); // Red
}
else
{
  Transform_MouseToRay.Translate.Z = ModelViewer.Position.Z;
  MouseToRaySize.Scale = 0.06;
  DatabaseCollisionColor.Color = vector4(1,1,1,0.1); // White
  MouseToRayColor.Color = vector4(1,1,1,1); // White
}]]>
      </Expression>
    </ZExpression>
    <KeyPress Comment="Left" Keys="&lt;QA">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Right" Keys="&gt;D">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Up" Keys="^ZW">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Down" Keys="_S">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Space" CharCode="32" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[MenuData += 1;
if(MenuData == DB_List.SizeDim1) MenuData = 0;

UpdateCollisionMesh();]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="Transform_MouseToRay" Translate="-27.4745 7.4556 -200">
      <Children>
        <UseMaterial Material="DatabaseRayMaterial"/>
        <RenderSetColor Name="MouseToRayColor" Color="1 1 1 0.2"/>
        <RenderTransformGroup Name="MouseToRaySize" Scale="0 0 0">
          <Children>
            <RenderMesh Mesh="Mesh_Col_Sphere"/>
          </Children>
        </RenderTransformGroup>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Model Name="ModelViewer" Position="0 0 -8" Rotation="-0.193 0.3762 0">
      <OnSpawn>
        <ZExpression>
          <Expression>
<![CDATA[
CurrentModel.RotationVelocity = vector3(rnd(), rnd(), 0);]]>
          </Expression>
        </ZExpression>
      </OnSpawn>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Rotate the model with arrow keys or decelerate rotation if not

if(Axis[1,0] && abs(CurrentModel.RotationVelocity.X) < 0.4) CurrentModel.RotationVelocity.X -= 0.5 * Axis[1,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.X -= App.DeltaTime * CurrentModel.RotationVelocity.X;

if(Axis[0,0] && abs(CurrentModel.RotationVelocity.Y) < 0.4) CurrentModel.RotationVelocity.Y += 0.5 * Axis[0,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.Y -= App.DeltaTime * CurrentModel.RotationVelocity.Y;


// Set the Collision Body to the current rotation of the model

zbtSetPosRot(Database_Body, CurrentModel.Position, CurrentModel.Rotation);]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="GhostMaterial"/>
        <ZExpression Comment="Render Mesh">
          <Expression>
<![CDATA[// I do it that way, because I use the same RenderMesh function to render things within the game too

int mesh = DB_List[MenuData];
RenderMesh(mesh);]]>
          </Expression>
        </ZExpression>
        <RenderTransformGroup Name="DatabaseTransformCollision" Scale="1 0.8 0.9">
          <Children>
            <RenderSetColor Name="DatabaseCollisionColor" Color="1 1 1 0.2"/>
            <RenderMesh Name="DatabaseCollisionMesh" Mesh="Mesh_Col_Sphere"/>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
    <Array Name="Axis" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
    <Array Name="DB_List" Type="1" SizeDim1="4"/>
    <Variable Name="MenuData" Type="1"/>
    <Variable Name="Database_Body" Type="9"/>
    <Group Comment="Materials">
      <Children>
        <Material Name="GhostMaterial" Shading="1" Color="1 1 1 0.2" SpecularColor="0 0 0 1" EmissionColor="0 0 0 1" Blend="1" ZBuffer="0"/>
        <Material Name="DatabaseRayMaterial" Shading="1" Color="1 0 0.502 1" Light="0" SpecularColor="1 1 1 1" EmissionColor="1 0.502 1 1" Shininess="1"/>
      </Children>
    </Group>
    <Group Comment="Meshes">
      <Children>
        <Mesh Name="Mesh_Trunk">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED983F6B9CCB1587C7B65204EE1606072EB752A3E54A95903E40E63B683B23175B48626AB5C242DDE20F2009D248A8122AB7573E8084AA354881B8BC315C9C2E8108FC669EF3EC78951B1CECC4E63AB02FEC683C7FCEF99DDF3967E68CDF3E49E90F8F13DF654A7FBCF4EFFEEF53CA99BF9B0BE72B6F5EF49FD6F1BCB99012FDF39594DFBC385FA9FFAE6BEA5C3E5FE93F7DF322A5CD0567F79652DA5B72D7DED2F9CADE12230777CC76978C77978C1FDC7555DBEDBBC593F395D3FBC593944EEFBBCBF395DB770777EC62B6FF9491FED3833B64DA67E5E602BB36175CE38818D27ED893DD4B2B1E2588EAF6DDCCA2D37B5AB6742FBB8E9691C51366174F44CB7A306305721A72B4801FE4C8A83BABF6AE3BBD87B79934FAD5F69729D1AA578D32ACBDA212E7ED3B98945BF1CBE4D4AE9026CEA6E57C65F1C456B4CE22A7BB94AB2933FB55726A1C8AB9AEE944AE2F9A8DF8424B417870272A2D12AD966205B2B428A48734E264C64C6809C6F4BE7C62639515A8F4E034BA02AD31F3D017EA9ACA746FC86CBAD8DBEC45A61C1A397228DBCC6EF56F76CE5776978DDBF58DF395EDB5B3BAFE2CEB97EDB5F395F58DDDE5F3959B1D7DB7D5D78360607CF590355717E264EFF34BE41C5D6B23F25FBD46D7F1EDEA21BAE4F0EA025DCF2FD1258747D7E87AF51A5DFAF7F8562FA38BF1D543D65C5DE853F63EBF44CED1B5DE417ED595F51AAD9CE853F977AFFE354EDC5B5796C8E81C195D22A3736474898CCE913B3932BAC05ECAE1DF12FECD21BF1045757D72EF54E6BEFEA9B3E8CDA13787DEC2BFC94664463CE4888702F290197DD7A88B5D9129D9B59129C57E446C8988CD11B12522B62829223647C496C8C7121994238372F05C22834A64508E0CCA91412532E8832DDA58639C1324B8929F2933C121B3334EE00A0971BE1567234A4B9C6059097142963821739C9085D30699AE71AF566BA332D5A5EDCD3A2DC26A2DAD115BE224CFA28E93BC20158D8C447EE5C8AFC2E9474BF6444667FACA61AFD22AD31D7E1C4CE07930A1DF750777154B076F8C9CDE0F26F4590916ACA64DFB9C5BF0825DB4D5A68EDCA4E584C33EDACA50C7294DCB99C2294DCB38FEA2653DA7342D72E08D16F9C4032DDAE18A16EDAC45EFE2C960D2507597830923221C4CF035385D231E2CC2F63A9BA7A8B2F807136200FC2923478429551E2A5A7810BF32DFBC40A69CA84BE4FDA7F08634FAA0441A39C2ACE3DA85047C4CDF35DA28AAAE43AF68654C6922518B08E55C8BF0267D2C755C4EE4CA59A5E9D9A60539482636D06E54C83C12C8C7F048B554749C1B813F7C01C3ADEF1A3D6524C82D9823F62B2AB538A25E574EF90CFEF58B3C8B4199EA129B16AD1E0E26BBCBE8DDEACBC3D5C560C2F93C989C4D3D7E743D98703E0F269CCF587D7CDB18E67C1E4C389F0713CE67B8652FE7F360C2F90C1B68E12E184C8E6F8D28B14D232DCB33ADF6DEEC804A1BD737C07396C1A3BDDB6BE0D95D068F31B6D59F451AE3DC4DE0D1D7ECE56E028F5184FC57AF65C6F89199E617BC2086F58DDE787B4D0C373BBD3123E2E98DCFB2A85C232AC7B7D77A63FACC5E5DF4C647D732B07AD81BBF7A2D33CF2F7BE3C6926B66BC6DAFB1727759EF30BBBD26FFC7B7BD71F7F2D5EBDE983E562B536ED37E6FCC6D8B2E3DA296A3EBDE983EF2F5AC329BAFC1DC22A137DEEA1B0F6268FE45027A9BAFD5EE4843D556B66811A7F1A35E67D5E8B8DE81B1AB0BD8A38F7CDBDD65E43BAB67C5A62FF491981979F55A2FE391A36BBD6C5F2DCA778D5CB9C6BD72A896AD3E16A9DDE8B265E466A7CD62A3AD1E77D648B00668B727F7A637A6779937A6771CB3518F95A8C74AD46325EAB112F558897AAC443D56A21E2B518F4D6FB7A8C74AD46325EAB112F558897AAC443D56A21E2B518F95A8C74AD46325EAB112F558399B5608B5D62A518F95A8C74AD463A5D5098C473D56A21E2B518F95A8C74AD46325EAB112F558D4245635DED7B4D6065BFDE1080ED1A22DBBCBC3D1CDCEFAC670D46C3FCBC3D1D9940DE5AF1E0E47E411F2AF2E86237CCD382B9F7FB09A71A4E13B2583598D373BC311598C4C75C93C9289257C91F68723E29FF1C606D8E4E4E87A385ADF403B793ADB2B93C7B768912579632F8C0D47E4200865EFF92598B7D790A6BD7A59AB1927E6679ED522597A180F4A685EC076B9527ED38E84E63B24CB9892E54D8DDA2E27722527482367D16BB4C883E3DA2E27CE1A694820BF8CBDE1E8F856261B33E09731D7E83B7DEADE692CC5DE16BD8C5B0DD6F320833065EE2F6ABC6A410643CA67D3EAB7E67AC6832913CFD47555526E3530E370C24EAB5FF6C264CAC433D5005AC09032F797157E549356C5D91AB555B3DC5FA0B2E65CDF004FE5239F4DEBCFED35462BEA4C2E83A446DC876A93713C0E1EEB52F612D5E0B15A463EF757BCE7B3F53035597B8F70FF6BAF2F29DF565AAD7C5F73BEE3D4EEDB4D5DED35874699F1CDE52B4C7EDAAB0D2DCA9767DF7DEE55A668D5EE6BAEA10289FEF285A837DB0B11C6EC379FF266943DDFAABE52E556CE7DC9CAB92FDCE65F90B7B72416F97ED4B3BE1FF5F8F42D19BE6EAF57D6E891872FDFC6A4ACC24063154E64D295F2A904ED923D2D92376358CCDAE23B5D690FDFE332D0E20A0932A0ED6296F3F606C7969629C854979CB7D734B6CB89FEBD7D57EFB8FAAE198E78E3D40CADFDDE98F7726FCCBD46CB1D4A04F7C6C43FEDDE526FCCBD463BBB97C985BA725A2959236D2EF4C6E4C2ECDE4C895BF5E1CDCB9AC5136F7F24741F6E70EE7474910BDEDD563EE802799A5638561AA7F7E86A9509BABA0E2DB36A6AF590358B27D639DADE2A347469FBB4D6AA18B8D76861837B8D160CBC338623F2D1F1E1887ADB95F4DD9B326BDC6B5F5BF69660DB35E291F9D3FBE188FECC23CA5497D8D425064E4BDE11C8673DE7A42739673B32E1C7B39AD31BF9F0C379EEF9DC759CC99ECC6A7F788B89CAFBC51B47549ECC9ED5E0E19CA4658438198E88135A2CE59C0455FF2978DABD0F9ECD05F078AF79DFA504126F70663927C1E39D850462033CDE2032E90D224BF22C4B535F044BC698F2956C1C1A6F8E28DF957ACA11B5CC56F20624D2CC0B6D375F64A0654DB0116B64C97C9119F348AFD9778D7B5D633CEB237D673C9B65722BABE6A319EA781B41CE145BF45D33F317AF4CE2A771455C99596A348A44227E4744EE4A51193F0D2D1CCA9208E54DE4293D4A8FEBAFEB1EA527E937F5F765FBFC87FFA3FA5B48B3FEE34FECD3439A7F1D7FF21FD63C94F0B86AFCD2B67C5EFFAE03C3F21790A9D5FF6EE3A37F6166E117E30FB9FA187B9FBEFE5DF7A4F69EA59FBB9F2BA267E9771FE5FF631EF9D4F57FED52FDBDAFEDFBEE591D79F68DFAF7DBEEC3CFFB8EFEC22771F5B118FBDAFDFF173FFEB9F278D72DA53F753F56CC3F7E555D0F7DF13572F921E7BF3C9F7FAA79F797EE71C8FCBEB6DF7F76FE7EEEFAB755E3DB69BEFF50477E98E7FBFCBEF88CF899DF17F3F3677EFE7CABFDAF71167D6E04FE2F67D1E745D7AF7716CD73F6BFE3F91FB5AEFB5BF7DBF4F7EEBBBAFEBB5F3D5F3ED6EFE6DFFC9B7FF36FFECDBFF937FFE6DFFC9B7FF36FFECDBFF9F74D7CFF0421898BCC]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Pyramid">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED51B191C2400C5CFCF01145B8063C43EC329E8C8836B836889CD1865D80A981223EFCF9172BE974F6CC270C0C19E739495E492B9DF405600B3DED4031B81E5A20B5A693392945DC329D4C27ECA5A775945E3A95D83BE25E39A2DD7CA306592E6B958AC89598DAD7402E6B95A895E17056D4F024FD6917FCC48D7942A43B9CCDEE02A1CF7AD0DC71749EA699D888A9DD07D2342AD95146B48A476666EB2A6745B7252BC794DCD3AED46296BF7A8A274B3D4D204716CE7174DC6BC51BB54A4C43F963AA36EDF26ACEDCF12E7AE6AC6D533E73DF60DE85EFD2B639F7E68DD866F3468A4D9FC57B0CB040C52BB2C00756BCF3CFF10A1A052C29FFA4C2AFACF0C30B7CF21FBC4274C9BF39CF63B9CFD47ADEFEFFF67B72E57D5E7A6E7B3B92D7]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Plane">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CDD4FB10DC23010BC3801092A4449C1082051D1E10118222BB081178884C400D904D2D1241D4C105150D222C8F3FE77424A6ADEF2CB3EDF9DEF570066902AAAB23C02930270B62AB71B606FE90497D5704056F3D9F79C724652E0BCF3322258E128224C46A02A45B8B8D76BE632BFF5E939A4E26983D675BDF50F6EDE87E87ED00CC141F080485AC5434E55A9BF6A95A319348F765575F37A95F8A7BD79C3A4FAFBD7337010C1F0268A1063C05B972206FE1D481005DCA0A117BD29E6F7863334D2877C7B92C118233CC86BA7CC4C3AB7DFD78D2E74A5399658C894FF501F4EDF7856]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Building">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED53BB4EC340105C830DE1D9A21408241A1054F448EE43CF1FA4A2E0135CF305B8434AFE800F081D4D6A40502252D20404029265E7C6CB3914D052E424AFC7B30FEFCDDE7544E458B08A9EC8E6A5013CB9C8B93D45AE2AD57284B77E2FE093B3FD0BD87E5FB5DBA205E371867BF6559425B014AAFD436029C4EA2FDC8099AC89AFFE21BDC82577DDD19EF771DDB16C3938855D3CAAAC757CB355D9C2FB3E38451656B765F113FBE8B6AC8E9C9CD1D21B7BF1D8D857D9C47FCB266A02C3A287F6807D62A7ED01F70B063CBCDCB57BC930DEBDAC409E5965133B2A9BD84BC045C039F9FAAEDD1B156024BDED019587E6ED812B5F96AC5C578C5988F12CC6034B8EF9EE0D31D9BD21260E0C26AA77B50C0C0B25A12DF1D53294AF7BC970221ECF3ACC8A7389918EDDCB59F00C700A3C15CEF3B43843CC79014F4E900CABF94CA9704DA56A0A51496A456D25A732F5F34F7D5CA5783BA8A4DB7877A82A19AF462F306F8A9F13DE1A9E3A4CC7E7C53BC58993E73DF29962D689CCD8A39AC86CC023C53B13789260DF3533BE216FFA69510DC3B805C0A92C54B949158DDCD418AF93DA1B799945CE99FF43C7E69BB7B891BE847F6681E5FD4AE5591BF2A42B32D435E3574347B193BA7DD439F3ADCB8332E6D52A6F583DB17E93F067F6F961FFCB64A9EA931DCE860A63EB70C6E23EF53DEC250DF1E380E7BF35C942C64F7CAFA8BF2DB77A67F57765E78FF829FE1DEB744DD73F595F5837EEC6]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Robot">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED977F6C545516C71FD3D21D89266B97342D742DA54442935742CA4E53A0A5A98C364B22EA1F6C79B89316FE6884A5DD140CE187ED542CAB16127E2C06636032D667C55F814D37B260B78602DBDAC6AC634424B54E82C82A14371A6C48DABC9DEF7CFAD2E7F26395B8D9DDEC3CC27039F7DC73BEE77BCE3DF7DEA274C308F80CC35FDDDD3DABB8E09DA387FFDCFD93EAC515B38ADB2B8E1E2EAF28EA8BFAABEA7776047C854F6D7A39E09BB671517FD43FF3774E73C077D70AA7D930E2E1DCF5015F6025E38E95015F7C5763B5F4C767D7057C17DB0756057CE75E4012A90BF8D6D7B2EAFC1ECD3EBD3DE09BF30AB3A5D1802F7B2DE39AFDD2647C3A24396B91E065FB0AA142B27193ECE0119B4898AD7E49D88A0F4C20212EE45E0963E2F56238BF238979C744BCF0804D6271E551FF8C5F10E3D6BAA87F61299AD8BC3E52F8813D24C445D4CFBE15F5CFEDDC702492B977F04A7BD41FEE691C886436FD85DF82632579AF5F6676E8E392BCCF3E98FD6924F3C4ABC7AF95E4196D835F95E4FDFC0F3B2F46329F78E6FD80B2096F3DF39547BC807CDD8EA87F7F3FBCE1256D4E24F3DC03AC0A4E8F6456D50D5444FD8DBF62EDD919F2D2982DCB9F2C88FA1F8C6CDBAF48610C96426F46FD39416A0654334F49133C5DD148E67DF7A0C3DA587724F3F9A2D133F24BED1577CBE3B1CFE4FDB93B4AF2DE5D052AF074E594E47D54861C244B3214F538DA24AB773FAA581EAF1287D406F92246628761A2D8592C56898E559521E18C3E2C9C66851092D36BF34BF266CF6016F9F2EA92BC6FEE0403160A7E2AFEC18624FF5EE50839BEB04CAE61983AC70B6374A828EA046C602652328B841CA149D47BD72BBFB0C1BE80E7C7BE157B305CF97949DE99B28C7EC5C52C19414EEEA83A1823A7FC527BCC527564012430402D1DEA104B58A606F0E83497E42DCB40E23447320F64A3B974B9698E66300BC34EB3691AC6E6A9A6B97A0159A01272E799A6391DCB540ED5452C444D9DC386DB79B45BDD7DAD7D4A3582C11D4B076C4D69A65956EBAD3D902027FB86619AAD6DCC521B6496EA259B2047823534594B85D44E314DBB139B609EB63B9279F4E2857AFD12CBB4DF97E49DBE86647C76AD24ACA5DE5AF24DB3F02C36610926C100C34395A659FA35FA83417515560DAE314DC741826534E99654055D949D4EA4F416324E6D53B16D0FAA5351F9542363ECE37D5C27D925A84FBA1955CA2AE2451366C817BB865D49D6DC9AD1AC2B572D110B16B006063A034CC230DD955AA26FD3E5A831626747D05BE084597A3EABE869440157E3276C32523A397EF14866872A2DABF46B7201F32DF9965578965DC01E21A2CE43CA63EE3CCB32A7935372BD74B965B9726A75F354CB5ABD80597677CBA8AA17B65B2F4987557871F7A06519467E97F014EC5355E477094FC13ECB721C3AC0C1ABAABAA6A164E524FB00B9C3BEE3A8B6F1C2AEC71718CEBF273C4D438A0ECBD8C123D6B080172CBB1EB50AFD83572DCBEEC472CBA86595D5E2BDF592A2C60B6374CEBF27663A0F295E66611239D1B10B887A708D244658BF70823E5C6107093192232CB3963C6293FC1A8665B5B6B1676BA708FF781F487AA456A9075691417A4E539A6224B32ECFB20657300033D418991A6738B9165F60C026B378641548C0463552876076F3A259320206583AB6DBB2823D1A1F38059F8B5FB4ACBAB7C96CC361CBDA72E491E39655DE5E78C2B25EDB43364FF458D6B6EDCD272D2B71FB4AE682ACE185B8F0426D7F586659590DD46DD9A284B52DE1855AFBC612CB7A32B46B9965DDFFD03BBFB61250C85AB0DEB2B6960BD5AC45644D38B7960B9B1514AAFB1F12AA27434295D540EC2E63F24B16E0813C0AC36B7BE4B1BC9D5CCBEF9623F25BF7B63C067B9EDEDE3736E71523EC38BAB54E8C1BABFBC60A9FEA58D93716DF8564FB8ABEB1691B912361766055DFD8B9179030CE5DD73776B11D8911EE1BBB6B45EEFABEB1C0CAEA9726AC151F90E6D91935C3275E5D92D11B3712A74C6F5CF7939AE1279ED9B33E34B2BF1F3B773F1A1AD13D41D86AF6F78DE946276B5EFBDBF6874616961AE1D0884E4FCDBA3A9A7535F57B3AD43796BD9658DA1EAC19DE3BF8785568646EE7CEE29A619D02F2B5B3433116F58546AAEA37BD2CCD45FDA11175484946CFD40C9F7BA0B83B34A2DB60CD70551DB160FFFC1E6166556954BEBCDEE12152A7280683BDF1D72FE337FFDEDEB8CE1AE199FDA938C1E3CC53A111DD51C5C9B8852427B0014EE45E6EB14FA4608037665905422F66C6D403597E3FA0787BE60B098C3DF66D6FFCDD55E0397E4D5983810BF535C33AB314D185B5BD719D658A0539AC2221B39D876231F534DB567FB36D8D633173FAB5F9BD71DD636B86EFBB6779756FFC9B3BCD8A9AE1E78B5A2FC562AB1764F427673FEF8D7F5486FE6F76C81AF8417EA55DBEA8A2E7EE105AAA2B385D38C10CC3835FA9DE88E5E0D558CCEE1CFA5859681A8AC5745E68769CFF2463304C5E400EAAD64BB6ADBE2D3C6973541B44D195D31B3F53067E986416240315AA1FB2402D91296A8CD88D702CA6F34E5C19E1DEF8B28C826362B8714035B3E188F80433968917FB7867477445C5A711AE19D61D929DA2F1A10EBCC8B2AB238FC8C991D76FC1BE584CE76C2CA66EAC2C53B7EC62F62FF54C16D81764E7D9B7B4CB881A4EC01C7D38349213240AF24274F0BFB56E022D554144E8901D2CB3160E436FCA26350FB7D376AB0EA93D6A92CA81432C8C578E875B6224762C907D6A9E4C513FEC1198A7F6C838B594DF65DBBA3FD8B6C6628F7D4427C11A55CD4EA18BD297D8EFB0FAC902C50BDBCCBA9C28476ECD882B7440051ED612110CB04F898B5F18F032C3986E037BEC3218A666A8A858B7720AE748E8C05E4DF62FD5853EFD909CBAB956C7A62A888E6A61B66534162BAB65173B4E2CA6FB9B982483EC50AA8EDA20FBF802099CF30B1E66E11F9BD41E3D015FD427B923A7540578580B127A021D18CCC442A4C44EAF607F8181ECD075C91A672B3D1C0BEC1D2C937D74584BCDD0A5D1A402B1D094269C8661DBBA5929C6A634DBD63D4D6819A3B379AABA197B19CDDA29E2812ECAB8255F95DC922F1E6AA7D8B6EE3FB2007B8E232F30C318260F5E95263AB9F3C400BEC0903B4F1D15860BF6D9B6FA8C760D3D8793058F4395D2A1F3B80865196CC448F72322BA225EE8CC8CD101C9E01A79C4F250A5FC0EAE915FF4E9C068B2CBF0EEB291AC872463B0E4B22D6C78E78C8013B8A24ED883F8C523911235B3C8C77B116C24D1C212B3605BBA5C27294C8299584048D6F0E59E29B6ADF345ABE8FCFC7222334B95923B2A9F3E464EF1F561996D67358417DA763C8CB53796D8B66EB9B6AD5BAE6D5B41AC05EB6D7B6BF95F37D876A44C12DD75A57F6CB7E48B5F9466C361ADE2847DE4B8EC149E90FD5F267EFDBF6D3E292F542C7927E3D4921BA3A2FE6302D59F760B9BEEC0B6AD3BB018132ADD816D5B7760DB0EF6C0A4F01C3835B7CDB6679E149E608FF0E85D206B42A57781EC088FDE05F24566E1C7DD05C2E3BE20A4E9AED26D1C39D6DCB78666C1E3BD9F83D3C5AF5544C1ADDE8D4E6374785F9005974FBD1DDC7784B8E5FDE26592D7040C83D095CBA6F7D50366DE4A20871FF28817DE2F78775F2BAEE5AC06B2E966592F1AF775A38CBB55341105EF265E526487370B6F377247A690B82FBB893CA2E37DEF4CE4DD45CBCBCB5B93F04C2EC802FCBBAF304541742077EB5CB1B82F3BC5083FA08237D0A2CF4BD0DD2913ABC0833570A2030FB0C45AC39864F8127F1D679291664C4EFCFD77FFB9B52FEF6C6A9C1AA7C6FF8B639F415FC9F88F23B9D9D8488ED292388D640704F324CFAF61A4DF50EECE4E3626224DFFA7D835722D785766783C2249BB4EA2DF61E7EF8E614CF548BE74B2123A39DFF122FB979CCB09CDACE4FFAF38E9897F7F96F8E3C5997683283E48AC39E3C8E75CA3C8B8BDEE7D7D27BF596FFF3E79F9A1BE6E0F676A9C1AA7C6FFCF636FE7FF3EDDFE87CABDA7DE84AFBF25FAED174E4E52E70B47DD3E3B31CA36BE7B3AA4DFF02CB8B99CDFC9B758255F5F3AA389DF512737F1FF5C63E2ECF025B1DD7EB7BCF5294FECE91EFBD74B7E5C8FFFEAD6012F939379FB312BCA497DA92FF5A5BED497FA525FEA4B7DA92FF5A5BED497FA525FEAFB2FFDFE01AF7AEE8B]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
      </Children>
    </Group>
    <Group Comment="Collision Meshes">
      <Children>
        <Mesh Name="Mesh_Col_Cone">
          <Producers>
            <MeshSphere Scale="0.5 0.5 0.5" ZSamples="3" RadialSamples="18"/>
            <MeshTransform Rotation="0.25 0 0"/>
            <MeshExpression Expression="v.Y = v.Y &lt; 0.25 ? -0.5 : 0.5;"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Sphere">
          <Producers>
            <MeshBox Scale="1 0.5 1" XCount="18" YCount="12" Grid2DOnly="255"/>
            <MeshExpression Scale="0.5 0.5 0.5" AutoNormals="0">
              <Expression>
<![CDATA[//

        float E, A, K, X, Y, Z;

        // Convert range to radians

        E = v.Y*PI; // Elevation
        A = v.X*PI; // Azimuth

        // Convert spherical coordinates into cartesian

        K = cos(E);

        X = sin(A)*K;
        Y = sin(E);
        Z = cos(A)*K;

        // Assign coordinates

        v.X = X;
        v.Y = Y;
        v.Z = Z;

        n.X = X;
        n.Y = Y;
        n.Z = Z;]]>
              </Expression>
            </MeshExpression>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Box">
          <Producers>
            <MeshBox Scale="0.5 0.5 0.5"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Cylinder">
          <Producers>
            <MeshSphere ZSamples="6" RadialSamples="14"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[//V : current vertex
//VarP is set to the number of Z samples;
//VarQ is the number of circonferences wanted in the upper and lower faces
float VarP;
int VarQ = 1;
float Psi;

VarP = 6;//OrigSphere.ZSamples;
if (v.Y != 0 || v.X != 0) {
    Psi = atan2(v.Y,v.X);
    v.X = cos(Psi); //*pow(1-abs(v.Z),2);
    v.Y = sin(Psi);
    //Normals
    //n.X = cos(Psi);
    //n.Y = sin(Psi);

    if (VarQ == 0) {
        v.Z /= 1 - 2/(VarP-1);
        }
    else { //VarQ != 0
        v.Z /= 1 - (VarQ+1)*2/(VarP-1);
        if (abs(v.Z)>1.001) {   //this is because we are using floating point precision
                                     //it is better to avoid stuff like "Z > 1" due to truncament errors

            //remember: tallest's high: (1/(1 - (VarQ+1)*2/(VarP-1));
            v.X *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);
            v.Y *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);

            v.Z = v.Z/(abs(v.Z));

        }
    } //ELSE

    //Normals
    if (abs(v.Z) < 0.999) {
        n.Z = 0; //lateral surface
        }
    else {
        if (pow(v.X,2) + pow(v.Y,2) < 0.999) {
               n.X = 0;  //Upper surface
               n.Y = 0;
               }
        else {
                 //Edges normals, you can decide what to use.
        }
    }  // End of normals section

    //soft edges      Work In Progress
    /*
    if (abs(v.Z) > 0.7 && pow(v.X,2) + pow(v.Y,2) > 0.7) {
        v.Z *= 0.9;
        v.Y *= 0.9;
        v.X *= 0.9;
        }
    // */

} //OUTER IF]]>
              </Expression>
            </MeshExpression>
            <MeshTransform Scale="0.5 0.5 0.5" Rotation="0.25 0 0"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Capsule">
          <Producers>
            <MeshBox Scale="0.5 0.5 1" XCount="15" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[float RX, SX, CX;

RX = v.X*PI*2;

SX = sin(RX); CX = cos(RX);

v.X = SX/2;
v.Z = CX/2;

n.X = v.X;
n.Y = 0;
n.Z = v.Z;]]>
              </Expression>
            </MeshExpression>
            <MeshBox Scale="0.5 0.5 1" XCount="15" YCount="3" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[float RX, SX, CX,
      RY, SY, CY;

RX = v.X*PI*2;
RY = v.Y*PI/2-PI/4;

SX = sin(RX); CX = cos(RX);
SY = sin(RY); CY = cos(RY);

v.X = SX*CY/2;
v.Y = SY/2;
v.Z = CX*CY/2;

n.X = v.X;
n.Y = v.Y;
n.Z = v.Z;

v.Y -= 0.5;]]>
              </Expression>
            </MeshExpression>
            <MeshCombine/>
            <MeshBox Scale="0.5 0.5 1" XCount="15" YCount="3" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[float RX, SX, CX,
      RY, SY, CY;

RX = v.X*PI*2;
RY = v.Y*PI/2+PI/4;

SX = sin(RX); CX = cos(RX);
SY = sin(RY); CY = cos(RY);

v.X = SX*CY/2;
v.Y = SY/2;
v.Z = CX*CY/2;

n.X = v.X;
n.Y = v.Y;
n.Z = v.Z;

v.Y += 0.5;]]>
              </Expression>
            </MeshExpression>
            <MeshCombine/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_CheckSizes">
          <Producers>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Position="-3 0 0"/>
            <MeshLoad Mesh="Mesh_Col_Sphere"/>
            <MeshTransform Position="-1.5 0 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Cone"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Cylinder"/>
            <MeshTransform Position="1.5 0 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Capsule"/>
            <MeshTransform Position="3 0 0"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
      </Children>
    </Group>
  </Content>
</ZApplication>
Last edited by Ats on Fri Jun 30, 2023 9:16 pm, edited 2 times in total.
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Kjell »

Hi Ats,

Are you you putting the transformation ( position / rotation / scale ) values in the SketchUp object names or something? Or are those simply the variable names your script generates from the object values?

Anyway, for a compound shape i'd use a parent object / dummy that ( for instance ) has a COMPOUND keyword and then place all objects "inside" that.
Don't have any experience with SketchUp, but you should be able to retrieve the local transformation values.

For example, this battleship uses 4 box shapes to make up a compound shape. I wouldn't worry about duplicate shapes ( Bay_L and Bay_R ) either, the amount of extra memory that takes isn't worth the effort ( imo ).

Image

K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

Thanks for the tips, Kjell. I'm advancing pretty well, but I'm stumbling on a little problem.
With this new method, it is simpler to export models to ZGE without having to center them, so that, as an example, the pyramid can be put on the ground by setting its Position.Z = 0, instead of Position.Z = -pyramidHeight/2.
But because of that, the pyramid model isn't centered in the database viewer. So I was wondering if there is a way to know the height of the model and center it automatically. Now that I'm writing that, I may have already asked that question years ago and decided to center everything instead :?
Here's a little example with a cube mesh that is exaggeratedly far from its center. Then the cube is centered using centerObject.Translate.Y = -10

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnUpdate>
    <ZExpression Expression="//centerObject.Translate.Y = MeshTest.Position.Y;"/>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="centerObject" Translate="0 -10 0">
      <Children>
        <RenderMesh Mesh="MeshTest"/>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Mesh Name="MeshTest">
      <Producers>
        <MeshBox/>
        <MeshTransform Position="0 10 0"/>
      </Producers>
    </Mesh>
  </Content>
</ZApplication>
Is there a way to know that -10 number by code? I think not...
I could easily detect that height in SketchUp and add it to the exporter. Or just go with centered meshes, but I would still need to know their height to put them on the ground.
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Kjell »

Hi Ats,
Ats wrote: Sun Jul 02, 2023 7:06 amWith this new method, it is simpler to export models to ZGE without having to center them, so that, as an example, the pyramid can be put on the ground by setting its Position.Z = 0, instead of Position.Z = -pyramidHeight/2.
Right, it's important to place your mesh origin correctly so that you can rotate / position your mesh the way you want ..
Ats wrote: Sun Jul 02, 2023 7:06 amBut because of that, the pyramid model isn't centered in the database viewer.
.. but if you want to use your meshes in two different ways ( in-game you want to easily position pyramids on the ground, but in the model viewer you want to have it rotate around its geometric center ), you need to provide a offset for one of them somehow.
Ats wrote: Sun Jul 02, 2023 7:06 amSo I was wondering if there is a way to know the height of the model and center it automatically.
There are basically 3 methods for this in ZGE, all which i would only use during development of your game to generate the offset coordinate ( and then for example store the results in a persistent array ).

- Use a MeshExpression to calculate the center.
- Decode the binary blob from a MeshImport component and calculate the center.
- Retrieve the vertex coordinates from the OpenGL vertex buffer object and calculate the center.

Here's a example of the MeshExpression route on the snippet from your last post ( result gets printed to the log-window when you run the project ).

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <RefreshContent Comment="This is just for convenience, so you get the result when running the project." Component="MeshTest"/>
  </OnLoaded>
  <OnUpdate>
    <ZExpression Expression="//centerObject.Translate.Y = MeshTest.Position.Y;"/>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="centerObject">
      <Children>
        <RenderMesh Mesh="MeshTest"/>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Mesh Name="MeshTest">
      <Producers>
        <MeshBox/>
        <MeshTransform Position="0 10 0"/>
        <CallComponent Component="GetMeshCenter"/>
      </Producers>
    </Mesh>
    <Group Name="GetMeshCenter">
      <Children>
        <Array Name="Bounds" SizeDim1="6"/>
        <Variable Name="Center" Type="7"/>
        <Variable Name="Vertex" Type="1"/>
        <ZExpression>
          <Expression>
<![CDATA[//

Vertex = 0;]]>
          </Expression>
        </ZExpression>
        <MeshExpression>
          <Expression>
<![CDATA[//

if(Vertex++)
{
  if(V.X < Bounds[0])Bounds[0] = V.X;
  if(V.Y < Bounds[1])Bounds[1] = V.Y;
  if(V.Z < Bounds[2])Bounds[2] = V.Z;

  if(V.X > Bounds[3])Bounds[3] = V.X;
  if(V.Y > Bounds[4])Bounds[4] = V.Y;
  if(V.Z > Bounds[5])Bounds[5] = V.Z;
}
else
{
  Bounds[0] = V.X;
  Bounds[1] = V.Y;
  Bounds[2] = V.Z;

  Bounds[3] = V.X;
  Bounds[4] = V.Y;
  Bounds[5] = V.Z;
}]]>
          </Expression>
        </MeshExpression>
        <ZExpression>
          <Expression>
<![CDATA[//

Center = vector3((Bounds[0]+Bounds[3])/2,
                 (Bounds[1]+Bounds[4])/2,
                 (Bounds[2]+Bounds[5])/2);

// Set centerObject translation based on center coordinates.

centerObject.Translate.X = -Center.X;
centerObject.Translate.Y = -Center.Y;
centerObject.Translate.Z = -Center.Z;

// For debugging purposes trace the result

trace(intToStr(Center.x)+","+intToStr(Center.y)+","+intToStr(Center.z));]]>
          </Expression>
        </ZExpression>
      </Children>
    </Group> <!-- GetMeshCenter -->

  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

Thanks Kjell. I'm not sure I understand what's going on in you snipet. What are the ZExpression and MeshExpression doing in a group? I didn't know this would even work. But another great example anyway :D

I can simply retrieve the offset in my sketchup ruby script and store it with the rest, it will be easier and ready for release too.

Here's my current progress:

I'm quite happy with the collision between the mouse cursor and the 3D object. My only concern is the number 950 that I found through "try and repeat", as I didn't find how to calculate that number correctly.

I had some little surprises too:
  • The sketchup and ZGE axis don't have the same rotation
  • Scaling an entity in SketchUp will complicate the way you can retrieve its rotation, so I had to use this: https://forums.sketchup.com/t/how-to-fi ... ty/65295/6
  • in ZGE, I had to use some math in order to keep the colision body at the right place after a rotation, for those the models that are forced centered, like the pyramid
So I put everything in a group: model and basic collision shapes. The script iterate through the content of that group and gives all the details that I need for ZGE.
Now I'm going to format these lines so I can copy paste them without and ounce of hesitation. Specialy the vector3() part that defines the scale of the visible collision shape, and it's position and rotation that are stored in the zbtAddChildShape line. I'll directly print: <MeshTransform Scale="0.6 0.43 2.96" Position="0.98 -0.17 1.01" Rotation="0 0.019 0"/>

.
Screenshot - 04_07_2023 , 21_22_34.png
Screenshot - 04_07_2023 , 21_22_34.png (96.21 KiB) Viewed 97845 times

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FixedFrameRate="60" ScreenMode="0" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[//

if (ANDROID)
{
  if(App.GLBase==0) this.ModuleName="libGLESv1_CM.so";
  else this.ModuleName="libGLESv2.so";
}
else if (LINUX)
{
  this.ModuleName = "libGL.so";
}
else
{
  this.ModuleName = "opengl32";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[const int GL_DEPTH_BUFFER_BIT = 0x0100;

void glClear(int mode){}


/* Boolean */
const int GL_ZERO = 0;
const int GL_ONE = 1;

/* BlendingFactorDest */
//const int GL_SRC_COLOR = 0x0300;
const int GL_ONE_MINUS_SRC_COLOR = 0x0301;
const int GL_SRC_ALPHA = 0x0302;
const int GL_ONE_MINUS_SRC_ALPHA = 0x0303;
//const int GL_DST_ALPHA = 0x0304;
//const int GL_ONE_MINUS_DST_ALPHA = 0x0305;

/* BlendingFactorSrc */
//const int GL_DST_COLOR = 0x0306;
const int GL_ONE_MINUS_DST_COLOR = 0x0307;
//const int GL_SRC_ALPHA_SATURATE = 0x0308;

/* EnableCap */
const int GL_BLEND = 0x0BE2;
//const int GL_COLOR_LOGIC_OP = 0x0BF2;

/* LogicOp */
//const int GL_INVERT = 0x150A;

void glDisable(int cap){}
void glEnable(int cap){}

//void glLogicOp(int opcode){}
//void glColor3f(float red, float green, float blue){}

void glBlendFunc(int sfactor, int dfactor){}

// Pour le screenshot
void glReadPixels(int x, int y, int width, int height, int format, int type, xptr data){}

// Coloration des vaisseaux sur un Hit avec une Light
//void glLightfv(int light, int pname, xptr params){}

// VR
//void glClear(int mask){}
void glViewport(int X, int Y, int Width, int Height){}
void glGetIntegerv(int pname, xptr params){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExternalLibrary Comment="Bullet 3D physics" ModuleName="ZgeBullet_x64">
      <BeforeInitExp>
<![CDATA[//

if(ANDROID)
{
  this.ModuleName = "./libZgeBullet.so";
}
else if(LINUX)
{
  this.ModuleName = "./ZgeBullet.so";
}
else
{
  this.ModuleName = "ZgeBullet_x64";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[/*
  ZgeBullet Library, a wrapper for the Bullet Physics Library.
  http://bulletphysics.org

  Project home
  https://github.com/Rado-1/ZgeBullet

  Download Windows DLL and Android shared library from
  https://github.com/Rado-1/ZgeBullet/releases

  Copyright (c) 2012-2016 Radovan Cervenka

  Version: 2.4 (2016-09-07)
*/


// Constants

// Triangle mesh types
//const int ZBT_TRIANGLE_CONVEX_HULL_MESH = 1;
//const int ZBT_TRIANGLE_CONCAVE_STATIC_MESH = 2;
//const int ZBT_TRIANGLE_CONCAVE_DEFORMABLE_MESH = 3;

// Activation states
//const int ZBT_ACTIVE_TAG = 1;
//const int ZBT_ISLAND_SLEEPING = 2;
//const int ZBT_WANTS_DEACTIVATION = 3;
//const int ZBT_DISABLE_DEACTIVATION = 4;
//const int ZBT_DISABLE_SIMULATION = 5;

// Default values of constraint limits
//const float ZBT_DEFAULT_HINGE_SOFTNESS = 0.9;
//const float ZBT_DEFAULT_HINGE_BIAS_FACTOR = 0.3;
//const float ZBT_DEFAULT_HINGE_RELAXATION_FACTOR = 1.0;
//const float ZBT_DEFAULT_CONE_TWIST_SOFTNESS = 1.0;
//const float ZBT_DEFAULT_CONE_TWIST_BIAS_FACTOR = 0.3;
//const float ZBT_DEFAULT_CONE_TWIST_RELAXATION_FACTOR = 1.0;

// Vehicle tunning defaults
//const float ZBT_DEFAULT_VEHICLE_SUSP_STIFFNESS = 5.88;
//const float ZBT_DEFAULT_VEHICLE_SUSP_COMPRESSION = 0.83;
//const float ZBT_DEFAULT_VEHICLE_SUSP_DAMPING = 0.88;
//const float ZBT_DEFAULT_VEHICLE_SUSP_MAX_SUSP_TRAVEL_CM = 500.0;
//const float ZBT_DEFAULT_VEHICLE_SUSP_FORCE = 6000.0;
//const float ZBT_DEFAULT_VEHICLE_FRICTION_SLIP = 10.5;

// Axes
//const int ZBT_AXIS_X_LINEAR = 0;
//const int ZBT_AXIS_Y_LINEAR = 1;
//const int ZBT_AXIS_Z_LINEAR = 2;
//const int ZBT_AXIS_X_ANGULAR = 3;
//const int ZBT_AXIS_Y_ANGULAR = 4;
//const int ZBT_AXIS_Z_ANGULAR = 5;

// Collision flags
const int ZBT_CF_STATIC_OBJECT= 1;
//const int ZBT_CF_KINEMATIC_OBJECT= 2;
//const int ZBT_CF_NO_CONTACT_RESPONSE = 4;
//const int ZBT_CF_CUSTOM_MATERIAL_CALLBACK = 8;
//const int ZBT_CF_CHARACTER_OBJECT = 16;
//const int ZBT_CF_DISABLE_SPU_COLLISION_PROCESSING = 64;


// Functions


// World
xptr zbtCreateWorld() {}
void zbtDestroyWorld(xptr world) {}
void zbtSetCurrentWorld(xptr world) {}
void zbtSetWorldGravity(float x, float y, float z) {}
void zbtStepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep) {}


// Collision shapes
//xptr zbtCreateStaticPlaneShape(float normalX, float normalY, float normalZ, float planeConstant) {}
xptr zbtCreateBoxShape(float x, float y, float z) {}
xptr zbtCreateSphereShape(float radius) {}
xptr zbtCreateScalableSphereShape(float radius) {}
xptr zbtCreateConeShape(float radius, float height) {}
xptr zbtCreateCylinderShape(float radius, float height) {}
xptr zbtCreateCapsuleShape(float radius, float height) {}
xptr zbtCreateCompoundShape() {}
xptr zbtAddChildShape(xptr compoundShape, xptr childShape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtRemoveChildShape(xptr compoundShape, xptr childShape) {}
xptr zbtCreateHeightfieldTerrainShape(xptr heightfieldData, int width, int length, float minHeight, float maxHeight, int upAxis, int bFlipQuadEdges, int bDiamondSubdivision) {}
xptr zbtCreateConvexHullShape(xptr points, int numPoints) {}
xptr zbtCreateMultiSphereShape(xptr positions, xptr radii, int numSpheres) {}
xptr zbtCreateTriangleMeshShape(xptr triangles, int numTriangles, int meshType) {}
void zbtUpdateDeformableTriangleMesh(xptr triangleMeshShape) {}
void zbtSetShapeLocalScaling(xptr shape, float x, float y, float z) {}
//void zbtSetShapeMargin(xptr shape, float margin) {}
//void zbtDeleteShape(xptr shape) {}
void zbtDeleteAllShapes() {}


// Rigid bodies
xptr zbtCreateRigidBodyXYZ(float mass, xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtCreateRigidBody(float mass, xptr shape, xptr position, xptr rotation) {}
void zbtDeleteRigidBody(xptr rigidBody) {}
//void zbtSetMass(xptr rigidBody, float mass) {}
//void zbtSetDamping(xptr rigidBody, float linearDamping, float angularDamping) {}
//void zbtSetLinearFactor(xptr rigidBody, float x, float y, float z) {}
//void zbtSetAngularFactor(xptr rigidBody, float x, float y, float z) {}
//void zbtSetGravity(xptr rigidBody, float x, float y, float z) {}
//void zbtSetLinearVelocity(xptr rigidBody, float x, float y, float z) {}
//void zbtGetLinearVelocity(xptr rigidBody,	ref float outX, ref float outY, ref float outZ) {}
//void zbtSetAngularVelocity(xptr rigidBody, float x, float y, float z) {}
//void zbtGetAngularVelocity(xptr rigidBody, ref float outX, ref float outY, ref float outZ) {}
//void zbtApplyCentralImpulse(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyCentralImpulseLocal(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyImpulse(xptr rigidBody, float x, float y, float z,	float relX, float relY, float relZ) {}
//void zbtApplyTorque(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueImpulse(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueLocal(xptr rigidBody, float x, float y, float z) {}
//void zbtApplyTorqueImpulseLocal(xptr rigidBody, float x, float y, float z) {}
void zbtSetSleepingThresholds(xptr rigidBody, float linear, float angular) {}


// Constraints and limits
//int zbtAreConnected(xptr rigidBodyA, xptr rigidBodyB) {}
//xptr zbtAddFixedConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bDisableCollision) {}
//xptr zbtAddPoint2PointConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ) {}
//xptr zbtAddPoint2PointConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, int bDisableCollision) {}
//xptr zbtAddHingeConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float axisX, float axisY, float axisZ) {}
//xptr zbtAddHingeConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, int bDisableCollision) {}
//void zbtSetHingeLimits(xptr hinge, float low, float high, float softness, float biasFactor, float relaxationFactor) {}
//void zbtEnableHingeAngularMotor(xptr hinge, int bEnableMotor, float targetVelocity, float maxMotorImpulse) {}
//xptr zbtAddConeTwistConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ) {}
//xptr zbtAddConeTwistConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bDisableCollision) {}
//void zbtSetConeTwistLimits(xptr twist, float swingSpanA, float swingSpanB, float twistSpan, float damping, float softness, float biasFactor, float relaxationFactor) {}
//void zbtEnableConeTwistMotor(xptr twist, int bEnableMotor, float maxMotorImpulse, float targetX, float targetY, float targetZ) {}
//xptr zbtAddSliderConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddSliderConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetSliderLimits(xptr slider, float linLower, float linUpper, float angLower, float angUpper) {}
//void zbtSetSliderSoftness(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtSetSliderRestitution(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtSetSliderDamping(xptr slider, float dirLin, float dirAng, float limLin, float limAng, float orthoLin, float orthoAng) {}
//void zbtEnableSliderLinearMotor(xptr slider, int bEnableMotor, float targetVelocity, float maxForce) {}
//void zbtEnableSliderAngularMotor(xptr slider, int bEnableMotor, float targetVelocity, float maxForce) {}
//xptr zbtAddGearConstraint(xptr rigidBodyA, xptr rigidBodyB, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, float ratio) {}
//void zbtSetGearConstraint(xptr gear, float axisAx, float axisAy, float axisAz, float axisBx, float axisBy, float axisBz, float ratio) {}
//xptr zbtAddGeneric6DofConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddGeneric6DofConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetGeneric6DofLimits(xptr dof, int axis, float lower, float upper) {}
//void zbtSetGeneric6DofLinearLimits(xptr dof, float lowerX, float lowerY, float lowerZ, float upperX, float upperY, float upperZ) {}
//void zbtSetGeneric6DofAngularLimits(xptr dof, float lowerX, float lowerY, float lowerZ, float upperX, float upperY, float upperZ) {}
//xptr zbtAddGeneric6DofSpringConstraint1(xptr rigidBody, float pivotX, float pivotY, float pivotZ, float rotX, float rotY, float rotZ, int bUseLinearReferenceWorldFrame) {}
//xptr zbtAddGeneric6DofSpringConstraint(xptr rigidBodyA, xptr rigidBodyB, float pivotAx, float pivotAy, float pivotAz, float pivotBx, float pivotBy, float pivotBz, float rotAx, float rotAy, float rotAz, float rotBx, float rotBy, float rotBz, int bUseLinearReferenceFrameA, int bDisableCollision) {}
//void zbtSetGeneric6DofSpring(xptr spring, int axis, int bEnableSpring, float stiffness, float damping, float equilibriumPoint) {}
//void zbtSetEnabled(xptr constraint, int bEnabled) {}
//void zbtDeleteConstraint(xptr constraint) {}


// Raycast vehicle
//void zbtSetVehicleTunning(float suspStiffness, float suspCompression, float suspDamping, float maxSuspTravelCm, float maxSuspForce, float frictionSlip) {}
//xptr zbtCreateRaycastVehicle(xptr carChassis, int rightAxis, int upAxis, int forwardAxis) {}
//int zbtAddWheel(xptr vehicle, float connectionPointX, float connectionPointY, float connectionPointZ, float directionX, float directionY, float directionZ, float wheelAxleX, float wheelAxleY, float wheelAxleZ, float wheelRadius, float suspRestLength, int bIsFrontWheel) {}
//void zbtSetWheelIsFront(xptr vehicle, int wheelId, int bIsFront) {}
//void zbtSetWheelRadius(xptr vehicle, int wheelId, float radius) {}
//void zbtSetWheelRollInfluence(xptr vehicle, int wheelId, float rollInfluence) {}
//void zbtSetWheelFrictionSlip(xptr vehicle, int wheelId, float frictionSlip) {}
//void zbtSetWheelSuspRestLength(xptr vehicle, int wheelId, float suspRestLength) {}
//void zbtSetWheelMaxSuspTravel(xptr vehicle, int wheelId, float maxSuspTravel) {}
//void zbtSetWheelSuspStiffness(xptr vehicle, int wheelId, float suspStiffness) {}
//void zbtSetWheelDampingCompression(xptr vehicle, int wheelId, float dampingCompression) {}
//void zbtSetWheelDampingRelaxation(xptr vehicle, int wheelId, float dampingRelaxation) {}
//void zbtSetWheelSteering(xptr vehicle, int wheelId, float steering) {}
//void zbtSetWheelEngineForce(xptr vehicle, int wheelId, float force) {}
//void zbtSetWheelBrake(xptr vehicle, int wheelId, float brake) {}
//void zbtResetVehicleSusp(xptr vehicle) {}
//float zbtGetVehicleCurrentSpeed(xptr vehicle) {}
//void zbtGetWheelPositionXYZ(xptr vehicle, int wheelId, ref float outX, ref float outY, ref float outZ) {}
//void zbtGetWheelPosition(xptr vehicle, int wheelId, xptr outPosition) {}
//void zbtGetWheelRotationXYZ(xptr vehicle, int wheelId, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetWheelRotation(xptr vehicle, int wheelId, xptr outRotation) {}
//void zbtGetWheelPosRotXYZ(xptr vehicle, int wheelId, ref float outX, ref float outY, ref float outZ, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetWheelPosRot(xptr vehicle, int wheelId, xptr outPosition, xptr outRotation) {}
//void zbtDeleteRaycastVehicle(xptr vehicle) {}


// Ghost object
//xptr zbtCreateGhostObject(xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
//void zbtDeleteGhostObject(xptr ghostObject) {}
//int zbtGetNumOverlappingObjects(xptr ghostObject) {}
//xptr zbtGetOverlappingObject(xptr ghostObject, int index) {}


// Kinematic character controller
//xptr zbtCreateKinematicCharacterController(xptr ghostObject, float stepHeight) {}
//void zbtDeleteKinematicCharacterController(xptr controller) {}
//void zbtSetCharacterUp(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterWalkDirection(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterVelocityForTimeInterval(xptr controller, float x, float y, float z, float timeInterval) {}
//void zbtCharacterWarp(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterFallSpeed(xptr controller, float fallSpeed) {}
//void zbtSetCharacterJumpSpeed(xptr controller, float jumpSpeed) {}
//void zbtSetCharacterMaxJumpHeight(xptr controller, float maxJumpHeight) {}
//int zbtCharacterCanJump(xptr controller) {}
//void zbtCharacterJump(xptr controller) {}
//void zbtSetCharacterGravity(xptr controller, float x, float y, float z) {}
//void zbtSetCharacterMaxSlope(xptr controller, float slope) {}
//void zbtSetCharacterUseGhostSweepTest(xptr controller, int bUseGhostObjectSweepTest) {}
//int zbtCharacterOnGround(xptr controller) {}
//void zbtCharacterReset(xptr controller) {}
//void zbtSetCharacterUpInterpolate(xptr controller, int bInterpolate) {}


// Collision objects (in general)
//void zbtSetFriction(xptr obj, float friction) {}
//void zbtSetRollingFriction(xptr obj, float friction) {}
//void zbtSetRestitution(xptr obj, float restitution) {}
//void zbtSetHitFraction(xptr obj, float hitFraction) {}
void zbtGetPositionXYZ(xptr obj, ref float outX, ref float outY, ref float outZ) {}
//void zbtGetPosition(xptr obj, xptr outPosition) {}
void zbtSetPositionXYZ(xptr obj, float x, float y, float z) {}
void zbtSetPosition(xptr obj, xptr position) {}
//void zbtGetRotationXYZ(xptr obj, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetRotation(xptr obj, xptr outRotation) {}
//void zbtSetRotationXYZ(xptr obj, float rx, float ry, float rz) {}
void zbtSetRotation(xptr obj, xptr rotation) {}
//void zbtGetRotationQuat(xptr obj, xptr outQuaternion) {}
//void zbtGetRotationDirection(xptr obj, xptr outDirection) {}
//void zbtSetRotationDirectionXYZ(xptr obj, float x, float y, float z) {}
//void zbtSetRotationDirection(xptr obj, xptr direction) {}
void zbtGetPosRotXYZ(xptr obj, ref float outX, ref float outY, ref float outZ, ref float outRx, ref float outRy, ref float outRz) {}
//void zbtGetPosRot(xptr obj, xptr outPosition, xptr outRotation) {}
//void zbtSetPosRotXYZ(xptr obj, float x, float y, float z, float rx, float ry, float rz) {}
void zbtSetPosRot(xptr obj, xptr position, xptr rotation) {}
//void zbtGetModelMatrix(xptr obj, xptr outMatrix) {}
//void zbtGetModelMatrixInv(xptr obj, xptr outMatrix) {}
void zbtSetCollisionFlags(xptr obj, int flags) {}
//int zbtIsActive(xptr obj) {}
//void zbtActivate(xptr obj, int bForceActivation) {}
void zbtSetActivationState(xptr obj, int newState) {}
//void zbtForceActivationState(xptr obj, int newState) {}
//void zbtSetDeactivationTime(xptr obj, float time) {}
//void zbtSetUserIndex(xptr obj, int index) {}
//int zbtGetUserIndex(xptr obj) {}
void zbtSetUserModel(xptr obj, model userModel) {}
model zbtGetUserModel(xptr obj) {}


// Collision detection
//void zbtSetIgnoreCollisionCheck(xptr objA, xptr objB, int bIgnoreCollisionCheck) {}
void zbtSetCollisionFilterGroupAndMask(xptr obj, int group, int mask) {}
//int zbtStartCollisionDetection() {}
//int zbtGetNextContact(ref xptr outObjA, ref xptr outObjB, xptr outPosA, xptr outPosB, xptr outNormal) {}
//void zbtGetCollidedObjects(int contactIndex, ref xptr outObjA, ref xptr outObjB, ref float outAppliedImpulse) {}
int zbtIsColliding(xptr obj) {}
//int zbtGetNumberOfCollisions(xptr obj) {}
int zbtIsCollidedWith(xptr objA, xptr objB) {}
//float zbtGetCollisionImpulse(xptr obj) {}
xptr zbtGetMainCollidedObject(xptr obj) {}


// Raycasting
xptr zbtRayTest(float fromX, float fromY, float fromZ, float toX, float toY, float toZ) {}
xptr zbtRayTestFiltered(float fromX, float fromY, float fromZ, float toX, float toY, float toZ, int filterGroup, int filterMask) {}
void zbtGetRayTestHitPointXYZ(ref float outX, ref float outY, ref float outZ) {}
//void zbtGetRayTestHitPoint(xptr outPosition) {}
//void zbtGetRayTestHitNormalXYZ(ref float outX, ref float outY, ref float outZ) {}
//void zbtGetRayTestHitNormal(xptr outNormal) {}]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary Comment="Google Doc - DATA" HasInitializer="1">
      <Source>
<![CDATA[/*
  In the real project, I have a shitload of models.
  So everything is listed in a Google Sheet doc where I can check if they are listed in the database,
  set their names...and then export this list.
*/

const int
  M_PLANE=0,
  M_PYRAMID=1,
  M_TRUNK=2,
  M_BUILDING=3,
  M_ROBOT=4;

{
  DB_List.SizeDim1=5;
  DB_List[0]=M_PYRAMID;
  DB_List[1]=M_TRUNK;
  DB_List[2]=M_BUILDING;
  DB_List[3]=M_PLANE;
  DB_List[4]=M_ROBOT;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Math">
      <Source>
<![CDATA[vec3 CalculatePosition(vec3 vector, vec3 rotation)
{
  float X, Y, Z, SX, CX, SY, CY, SZ, CZ;
  X = rotation.X*PI*2;
  Y = rotation.Y*PI*2;
  Z = rotation.Z*PI*2;
  SX = sin(X); CX = cos(X);
  SY = sin(Y); CY = cos(Y);
  SZ = sin(Z); CZ = cos(Z);
  return vector3(vector.X * CY*CZ + vector.Y * (SX*SY*CZ-CX*SZ) + vector.Z * (CX*SY*CZ+SX*SZ),
                 vector.X * CY*SZ + vector.Y * (SX*SY*SZ+CX*CZ) + vector.Z * (CX*SY*SZ-SX*CZ),
                 vector.Y * SX*CY - vector.X * SY + vector.Z * CX*CY );
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Body Shapes">
      <Source>
<![CDATA[const float

  CAM_FOV = 0.4142;     // = tan(App.FOV(45) / 360 * PI);



/*
  We need to create all the collision shapes here
*/

xptr

  World,

  SHAPE_BOX_2000x5000x4000,
  SHAPE_CONE_6000x4000,
  SHAPE_SPHERE_1000,
  SHAPE_SPHERE_1330x1000x4410,
  SHAPE_CAPSULE_3000x2530,
  SHAPE_CYLINDER_1000x3000,
  SHAPE_BOX_600x430x2960,
  SHAPE_BOX_2120x1000x2120,
  SHAPE_BOX_4240x1000x4240,
  SHAPE_BOX_3180x1000x3180,
  SHAPE_CONE_1520x1000,
  SHAPE_PYRAMID,
  SHAPE_PLANE;



vec3 ScaleRigidBody(int type)
{
  switch(type)
  {
    case M_TRUNK:     return vector3(1.0, 3.0, 1.0);
    case M_BUILDING:  return vector3(2.0, 5.0, 4.0);
    case M_ROBOT:     return vector3(3.0, 2.53, 3.0);
  }
  // Or return no scale
  return vector3(1.0, 1.0, 1.0);
}


xptr CreateRigidBody(model object, int type)
{
  switch(type)
  {
    case M_PLANE:     return zbtCreateRigidBody(1, SHAPE_PLANE, object.Position, object.Rotation);
    case M_PYRAMID:   return zbtCreateRigidBody(1, SHAPE_PYRAMID, object.Position, object.Rotation);
    case M_TRUNK:     return zbtCreateRigidBody(1, SHAPE_CYLINDER_1000x3000, object.Position, object.Rotation);
    case M_BUILDING:  return zbtCreateRigidBody(1, SHAPE_BOX_2000x5000x4000, object.Position, object.Rotation);
    case M_ROBOT:     return zbtCreateRigidBody(1, SHAPE_CAPSULE_3000x2530, object.Position, object.Rotation);
  }

  // Got to return something, otherwise ZGEBullet will crash
  trace("\nThere is a problem with CreateRigidBody type=" + intToStr(type) + "\n");
  return zbtCreateRigidBody(1, SHAPE_SPHERE_1000, object.Position, object.Rotation);
}


void RenderMesh(int mesh)
{
  switch(mesh)
  {
    case M_PLANE:     @RenderMesh(Mesh:Mesh_Plane); return;
    case M_PYRAMID:   @RenderMesh(Mesh:Mesh_Pyramid); return;
    case M_TRUNK:     @RenderMesh(Mesh:Mesh_Trunk); return;
    case M_BUILDING:  @RenderMesh(Mesh:Mesh_Building); return;
    case M_ROBOT:     @RenderMesh(Mesh:Mesh_Robot); return;
  }
}



/*
  Set the visible collision Mesh, that has no collision at all...
*/

void UpdateDatabaseCollisionMesh()
{
  int CurrentView = DB_List[MenuData];

  DatabaseTransformCollision.Scale = 1;
  DatabaseTransformCollision.Translate = 0;
  DatabaseTransformCollision.Rotate = 0;

  DatabaseTransform.Scale = 1;
  DatabaseTransform.Translate = 0;
  DatabaseTransform.Rotate = 0;

  ModelViewerBodyCenter = 0;

  switch (CurrentView)
  {
    case M_BUILDING:  DatabaseCollisionMesh.Mesh = Mesh_Col_Box; break;
    case M_PLANE:     DatabaseCollisionMesh.Mesh = Mesh_Col_Plane; break;
    case M_TRUNK:     DatabaseCollisionMesh.Mesh = Mesh_Col_Cylinder; break;
    case M_PYRAMID:
      ModelViewerBodyCenter.Y = -2;
      DatabaseCollisionMesh.Mesh = Mesh_Col_Pyramid;
      break;
    case M_ROBOT:     DatabaseCollisionMesh.Mesh = Mesh_Col_Capsule; break;
  }

  DatabaseTransform.Translate = ModelViewerBodyCenter;
  DatabaseTransformCollision.Scale = ScaleRigidBody(CurrentView);

  // Set the Body, that has the real collision, but is not visible
  if (Database_Body != null) zbtDeleteRigidBody(Database_Body);
  Database_Body = CreateRigidBody(ModelViewer, CurrentView);

  vec3 ModelViewerPosition = ModelViewer.Position;
  ModelViewerPosition.X += ModelViewerBodyCenter.X;
  ModelViewerPosition.Y += ModelViewerBodyCenter.Y;
  ModelViewerPosition.Z += ModelViewerBodyCenter.Z;
  zbtSetPosRot(Database_Body, ModelViewerPosition, ModelViewer.Rotation);
}]]>
      </Source>
    </ZLibrary>
    <ZExpression Comment="init physics">
      <Expression>
<![CDATA[// Init physical world

World = zbtCreateWorld();
zbtSetCurrentWorld(World);
zbtSetWorldGravity(0, 0, 0);


// Create collision shapes
// Collision shapes can not be set in ZLibrary, as it will crash
// Details about shapes: https://stephengold.github.io/Minie/minie/minie-library-tutorials/shape.html
// or https://docs.panda3d.org/1.10/python/programming/physics/bullet/collision-shapes

//--- BASIC SHAPES -------------------------------------------------------------

SHAPE_BOX_2000x5000x4000 = zbtCreateBoxShape(1.0, 2.5, 2.0);

SHAPE_CONE_6000x4000 = zbtCreateConeShape(3.0, 4.0);

SHAPE_CYLINDER_1000x3000 = zbtCreateCylinderShape(0.5, 1.5);

SHAPE_CAPSULE_3000x2530 = zbtCreateCapsuleShape(1.5, 2.53);

SHAPE_SPHERE_1000 = zbtCreateSphereShape(0.5);

SHAPE_SPHERE_1330x1000x4410 = zbtCreateScalableSphereShape(1.0);
zbtSetShapeLocalScaling(SHAPE_SPHERE_1330x1000x4410, 0.665, 0.5, 2.205);

SHAPE_BOX_600x430x2960 = zbtCreateBoxShape(0.3, 0.215, 1.48);

SHAPE_BOX_2120x1000x2120 = zbtCreateBoxShape(1.06, 0.5, 1.06);
SHAPE_BOX_4240x1000x4240 = zbtCreateBoxShape(2.12, 0.5, 2.12);
SHAPE_BOX_3180x1000x3180 = zbtCreateBoxShape(1.59, 0.5, 1.59);
SHAPE_CONE_1520x1000 = zbtCreateConeShape(0.76, 1.0);

//--- COMBINED SHAPES ----------------------------------------------------------

SHAPE_PLANE = zbtCreateCompoundShape();
zbtAddChildShape(SHAPE_PLANE, SHAPE_SPHERE_1330x1000x4410, 0.0,-0.1,-0.24, 0.98,0.0,0.0);
zbtAddChildShape(SHAPE_PLANE, SHAPE_BOX_600x430x2960, 0.98,-0.17,1.01, 0.0,0.088,0.0);
zbtAddChildShape(SHAPE_PLANE, SHAPE_BOX_600x430x2960, -0.98,-0.17,1.01, 0.0,0.915,0.0);

SHAPE_PYRAMID = zbtCreateCompoundShape();
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_2120x1000x2120, 0.0,2.5,0.0, 0.0,0.875,0.0);
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_4240x1000x4240, 0.0,0.5,0.0, 0.0,0.875,0.0);
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_3180x1000x3180, 0.0,1.5,0.0, 0.0,0.875,0.0);
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_CONE_1520x1000, 0.0,3.5,0.01, 0.0,0.0,0.0);]]>
      </Expression>
    </ZExpression>
    <SpawnModel Model="ModelViewer" Position="0 0 -6" SpawnStyle="1"/>
    <ZExpression>
      <Expression>
<![CDATA[MenuData = 0;

Database_Body = null;

UpdateDatabaseCollisionMesh();]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression Comment="Mouse raycast">
      <Expression>
<![CDATA[Axis[0,0] = 0;
Axis[1,0] = 0;

zbtStepSimulation(App.DeltaTime, 0, 0);

// Mouse Raycast

float x = App.MousePosition.X * CAM_FOV * App.ViewportWidth / App.ViewportHeight; // If you're using a constant aspectRatio, you can swap out "App.ViewportWidth/App.ViewportHeight" with a specific value.
float y = App.MousePosition.Y * CAM_FOV;

Transform_MouseToRay.Translate.X = x;
Transform_MouseToRay.Translate.Y = y;
Transform_MouseToRay.Translate.Z = App.CameraPosition.Z - 1;

float x2 = x * 950; // WTF, why 950?
float y2 = y * 950;

if (zbtRayTest(x, y, App.CameraPosition.Z, x2, y2, -1000) != null) // The body has been hit by the ray
{
  MouseToRaySize.Scale = 0.006;
  DatabaseCollisionColor.Color = vector4(1,0,0,0.6); // Red
  MouseToRayColor.Color = vector4(1,1,0,1); // Red
}
else
{
  MouseToRaySize.Scale = 0.005;
  DatabaseCollisionColor.Color = vector4(1,1,1,0.1); // White
  MouseToRayColor.Color = vector4(1,1,1,1); // White
}]]>
      </Expression>
    </ZExpression>
    <KeyPress Comment="Left" Keys="&lt;QA">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Right" Keys="&gt;D">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Up" Keys="^ZW">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Down" Keys="_S">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Space" CharCode="32" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[MenuData += 1;
if(MenuData == DB_List.SizeDim1) MenuData = 0;

UpdateDatabaseCollisionMesh();]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="Transform_MouseToRay" Translate="-1.235 0.4142 9">
      <Children>
        <UseMaterial Material="DatabaseRayMaterial"/>
        <RenderSetColor Name="MouseToRayColor" Color="1 1 1 1"/>
        <RenderTransformGroup Name="MouseToRaySize" Scale="0.005 0.005 0.005">
          <Children>
            <RenderMesh Mesh="Mesh_Col_Sphere"/>
          </Children>
        </RenderTransformGroup>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Model Name="ModelViewer" Position="0 0 -6" Rotation="0.2206 1.8588 0">
      <Definitions>
        <Variable Name="ModelViewerBodyCenter" Type="7"/>
      </Definitions>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Rotate the model with arrow keys or decelerate rotation if not

if(Axis[1,0] && abs(CurrentModel.RotationVelocity.X) < 0.4) CurrentModel.RotationVelocity.X -= 0.5 * Axis[1,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.X -= App.DeltaTime * CurrentModel.RotationVelocity.X;

if(Axis[0,0] && abs(CurrentModel.RotationVelocity.Y) < 0.4) CurrentModel.RotationVelocity.Y += 0.5 * Axis[0,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.Y -= App.DeltaTime * CurrentModel.RotationVelocity.Y;


// Set the Collision Body position and rotation

vec3 ModelViewerPosition = CalculatePosition(ModelViewerBodyCenter, ModelViewer.Rotation);
ModelViewerPosition.X += ModelViewer.Position.X;
ModelViewerPosition.Y += ModelViewer.Position.Y;
ModelViewerPosition.Z += ModelViewer.Position.Z;
zbtSetPosRot(Database_Body, ModelViewerPosition, ModelViewer.Rotation);]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="GhostMaterial"/>
        <RenderTransformGroup Name="DatabaseTransform" Translate="0 -2 0">
          <Children>
            <ZExpression Comment="Render Mesh">
              <Expression>
<![CDATA[// I do it that way, because I use the same RenderMesh function to render things within the game too

int mesh = DB_List[MenuData];
RenderMesh(mesh);]]>
              </Expression>
            </ZExpression>
            <RenderTransformGroup Name="DatabaseTransformCollision">
              <Children>
                <RenderSetColor Name="DatabaseCollisionColor" Color="1 1 1 0.1"/>
                <RenderMesh Name="DatabaseCollisionMesh" Mesh="Mesh_Col_Pyramid"/>
              </Children>
            </RenderTransformGroup>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
    <Array Name="Axis" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
    <Array Name="DB_List" Type="1" SizeDim1="5"/>
    <Variable Name="MenuData" Type="1"/>
    <Variable Name="Database_Body" Type="9"/>
    <Group Comment="Materials">
      <Children>
        <Material Name="GhostMaterial" Shading="1" Color="1 1 1 0.2" SpecularColor="0 0 0 1" EmissionColor="0 0 0 1" Blend="1" ZBuffer="0"/>
        <Material Name="DatabaseRayMaterial" Shading="1" Color="1 0 0.502 1" Light="0" SpecularColor="1 1 1 1" EmissionColor="1 0.502 1 1" Shininess="1"/>
      </Children>
    </Group>
    <Group Comment="Meshes">
      <Children>
        <Mesh Name="Mesh_Trunk">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED983F6B9CCB1587C7B65204EE1606072EB752A3E54A95903E40E63B683B23175B48626AB5C242DDE20F2009D248A8122AB7573E8084AA354881B8BC315C9C2E8108FC669EF3EC78951B1CECC4E63AB02FEC683C7FCEF99DDF3967E68CDF3E49E90F8F13DF654A7FBCF4EFFEEF53CA99BF9B0BE72B6F5EF49FD6F1BCB99012FDF39594DFBC385FA9FFAE6BEA5C3E5FE93F7DF322A5CD0567F79652DA5B72D7DED2F9CADE12230777CC76978C77978C1FDC7555DBEDBBC593F395D3FBC593944EEFBBCBF395DB770777EC62B6FF9491FED3833B64DA67E5E602BB36175CE38818D27ED893DD4B2B1E2588EAF6DDCCA2D37B5AB6742FBB8E9691C51366174F44CB7A306305721A72B4801FE4C8A83BABF6AE3BBD87B79934FAD5F69729D1AA578D32ACBDA212E7ED3B98945BF1CBE4D4AE9026CEA6E57C65F1C456B4CE22A7BB94AB2933FB55726A1C8AB9AEE944AE2F9A8DF8424B417870272A2D12AD966205B2B428A48734E264C64C6809C6F4BE7C62639515A8F4E034BA02AD31F3D017EA9ACA746FC86CBAD8DBEC45A61C1A397228DBCC6EF56F76CE5776978DDBF58DF395EDB5B3BAFE2CEB97EDB5F395F58DDDE5F3959B1D7DB7D5D78360607CF590355717E264EFF34BE41C5D6B23F25FBD46D7F1EDEA21BAE4F0EA025DCF2FD1258747D7E87AF51A5DFAF7F8562FA38BF1D543D65C5DE853F63EBF44CED1B5DE417ED595F51AAD9CE853F977AFFE354EDC5B5796C8E81C195D22A3736474898CCE913B3932BAC05ECAE1DF12FECD21BF1045757D72EF54E6BEFEA9B3E8CDA13787DEC2BFC94664463CE4888702F290197DD7A88B5D9129D9B59129C57E446C8988CD11B12522B62829223647C496C8C7121994238372F05C22834A64508E0CCA91412532E8832DDA58639C1324B8929F2933C121B3334EE00A0971BE1567234A4B9C6059097142963821739C9085D30699AE71AF566BA332D5A5EDCD3A2DC26A2DAD115BE224CFA28E93BC20158D8C447EE5C8AFC2E9474BF6444667FACA61AFD22AD31D7E1C4CE07930A1DF750777154B076F8C9CDE0F26F4590916ACA64DFB9C5BF0825DB4D5A68EDCA4E584C33EDACA50C7294DCB99C2294DCB38FEA2653DA7342D72E08D16F9C4032DDAE18A16EDAC45EFE2C960D2507597830923221C4CF035385D231E2CC2F63A9BA7A8B2F807136200FC2923478429551E2A5A7810BF32DFBC40A69CA84BE4FDA7F08634FAA0441A39C2ACE3DA85047C4CDF35DA28AAAE43AF68654C6922518B08E55C8BF0267D2C755C4EE4CA59A5E9D9A60539482636D06E54C83C12C8C7F048B554749C1B813F7C01C3ADEF1A3D6524C82D9823F62B2AB538A25E574EF90CFEF58B3C8B4199EA129B16AD1E0E26BBCBE8DDEACBC3D5C560C2F93C989C4D3D7E743D98703E0F269CCF587D7CDB18E67C1E4C389F0713CE67B8652FE7F360C2F90C1B68E12E184C8E6F8D28B14D232DCB33ADF6DEEC804A1BD737C07396C1A3BDDB6BE0D95D068F31B6D59F451AE3DC4DE0D1D7ECE56E028F5184FC57AF65C6F89199E617BC2086F58DDE787B4D0C373BBD3123E2E98DCFB2A85C232AC7B7D77A63FACC5E5DF4C647D732B07AD81BBF7A2D33CF2F7BE3C6926B66BC6DAFB1727759EF30BBBD26FFC7B7BD71F7F2D5EBDE983E562B536ED37E6FCC6D8B2E3DA296A3EBDE983EF2F5AC329BAFC1DC22A137DEEA1B0F6268FE45027A9BAFD5EE4843D556B66811A7F1A35E67D5E8B8DE81B1AB0BD8A38F7CDBDD65E43BAB67C5A62FF491981979F55A2FE391A36BBD6C5F2DCA778D5CB9C6BD72A896AD3E16A9DDE8B265E466A7CD62A3AD1E77D648B00668B727F7A637A6779937A6771CB3518F95A8C74AD46325EAB112F558897AAC443D56A21E2B518F4D6FB7A8C74AD46325EAB112F558897AAC443D56A21E2B518F95A8C74AD46325EAB112F558399B5608B5D62A518F95A8C74AD463A5D5098C473D56A21E2B518F95A8C74AD46325EAB112F558D4245635DED7B4D6065BFDE1080ED1A22DBBCBC3D1CDCEFAC670D46C3FCBC3D1D9940DE5AF1E0E47E411F2AF2E86237CCD382B9F7FB09A71A4E13B2583598D373BC311598C4C75C93C9289257C91F68723E29FF1C606D8E4E4E87A385ADF403B793ADB2B93C7B768912579632F8C0D47E4200865EFF92598B7D790A6BD7A59AB1927E6679ED522597A180F4A685EC076B9527ED38E84E63B24CB9892E54D8DDA2E27722527482367D16BB4C883E3DA2E27CE1A694820BF8CBDE1E8F856261B33E09731D7E83B7DEADE692CC5DE16BD8C5B0DD6F320833065EE2F6ABC6A410643CA67D3EAB7E67AC6832913CFD47555526E3530E370C24EAB5FF6C264CAC433D5005AC09032F797157E549356C5D91AB555B3DC5FA0B2E65CDF004FE5239F4DEBCFED35462BEA4C2E83A446DC876A93713C0E1EEB52F612D5E0B15A463EF757BCE7B3F53035597B8F70FF6BAF2F29DF565AAD7C5F73BEE3D4EEDB4D5DED35874699F1CDE52B4C7EDAAB0D2DCA9767DF7DEE55A668D5EE6BAEA10289FEF285A837DB0B11C6EC379FF266943DDFAABE52E556CE7DC9CAB92FDCE65F90B7B72416F97ED4B3BE1FF5F8F42D19BE6EAF57D6E891872FDFC6A4ACC24063154E64D295F2A904ED923D2D92376358CCDAE23B5D690FDFE332D0E20A0932A0ED6296F3F606C7969629C854979CB7D734B6CB89FEBD7D57EFB8FAAE198E78E3D40CADFDDE98F7726FCCBD46CB1D4A04F7C6C43FEDDE526FCCBD463BBB97C985BA725A2959236D2EF4C6E4C2ECDE4C895BF5E1CDCB9AC5136F7F24741F6E70EE7474910BDEDD563EE802799A5638561AA7F7E86A9509BABA0E2DB36A6AF590358B27D639DADE2A347469FBB4D6AA18B8D76861837B8D160CBC338623F2D1F1E1887ADB95F4DD9B326BDC6B5F5BF69660DB35E291F9D3FBE188FECC23CA5497D8D425064E4BDE11C8673DE7A42739673B32E1C7B39AD31BF9F0C379EEF9DC759CC99ECC6A7F788B89CAFBC51B47549ECC9ED5E0E19CA4658438198E88135A2CE59C0455FF2978DABD0F9ECD05F078AF79DFA504126F70663927C1E39D850462033CDE2032E90D224BF22C4B535F044BC698F2956C1C1A6F8E28DF957ACA11B5CC56F20624D2CC0B6D375F64A0654DB0116B64C97C9119F348AFD9778D7B5D633CEB237D673C9B65722BABE6A319EA781B41CE145BF45D33F317AF4CE2A771455C99596A348A44227E4744EE4A51193F0D2D1CCA9208E54DE4293D4A8FEBAFEB1EA527E937F5F765FBFC87FFA3FA5B48B3FEE34FECD3439A7F1D7FF21FD63C94F0B86AFCD2B67C5EFFAE03C3F21790A9D5FF6EE3A37F6166E117E30FB9FA187B9FBEFE5DF7A4F69EA59FBB9F2BA267E9771FE5FF631EF9D4F57FED52FDBDAFEDFBEE591D79F68DFAF7DBEEC3CFFB8EFEC22771F5B118FBDAFDFF173FFEB9F278D72DA53F753F56CC3F7E555D0F7DF13572F921E7BF3C9F7FAA79F797EE71C8FCBEB6DF7F76FE7EEEFAB755E3DB69BEFF50477E98E7FBFCBEF88CF899DF17F3F3677EFE7CABFDAF71167D6E04FE2F67D1E745D7AF7716CD73F6BFE3F91FB5AEFB5BF7DBF4F7EEBBBAFEBB5F3D5F3ED6EFE6DFFC9B7FF36FFECDBFF937FFE6DFFC9B7FF36FFECDBFF9F74D7CFF0421898BCC]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Pyramid">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED51B191C2400C5CFCF01145B8063C43EC329E8C8836B836889CD1865D80A981223EFCF9172BE974F6CC270C0C19E739495E492B9DF405600B3DED4031B81E5A20B5A693392945DC329D4C27ECA5A775945E3A95D83BE25E39A2DD7CA306592E6B958AC89598DAD7402E6B95A895E17056D4F024FD6917FCC48D7942A43B9CCDEE02A1CF7AD0DC71749EA699D888A9DD07D2342AD95146B48A476666EB2A6745B7252BC794DCD3AED46296BF7A8A274B3D4D204716CE7174DC6BC51BB54A4C43F963AA36EDF26ACEDCF12E7AE6AC6D533E73DF60DE85EFD2B639F7E68DD866F3468A4D9FC57B0CB040C52BB2C00756BCF3CFF10A1A052C29FFA4C2AFACF0C30B7CF21FBC4274C9BF39CF63B9CFD47ADEFEFFF67B72E57D5E7A6E7B3B92D7]]>
              </MeshData>
            </MeshImport>
            <MeshTransform Position="0 2 0"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Plane">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CDD4FB10DC23010BC3801092A4449C1082051D1E10118222BB081178884C400D904D2D1241D4C105150D222C8F3FE77424A6ADEF2CB3EDF9DEF570066902AAAB23C02930270B62AB71B606FE90497D5704056F3D9F79C724652E0BCF3322258E128224C46A02A45B8B8D76BE632BFF5E939A4E26983D675BDF50F6EDE87E87ED00CC141F080485AC5434E55A9BF6A95A319348F765575F37A95F8A7BD79C3A4FAFBD7337010C1F0268A1063C05B972206FE1D481005DCA0A117BD29E6F7863334D2877C7B92C118233CC86BA7CC4C3AB7DFD78D2E74A5399658C894FF501F4EDF7856]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Building">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED53BB4EC340105C830DE1D9A21408241A1054F448EE43CF1FA4A2E0135CF305B8434AFE800F081D4D6A40502252D20404029265E7C6CB3914D052E424AFC7B30FEFCDDE7544E458B08A9EC8E6A5013CB9C8B93D45AE2AD57284B77E2FE093B3FD0BD87E5FB5DBA205E371867BF6559425B014AAFD436029C4EA2FDC8099AC89AFFE21BDC82577DDD19EF771DDB16C3938855D3CAAAC757CB355D9C2FB3E38451656B765F113FBE8B6AC8E9C9CD1D21B7BF1D8D857D9C47FCB266A02C3A287F6807D62A7ED01F70B063CBCDCB57BC930DEBDAC409E5965133B2A9BD84BC045C039F9FAAEDD1B156024BDED019587E6ED812B5F96AC5C578C5988F12CC6034B8EF9EE0D31D9BD21260E0C26AA77B50C0C0B25A12DF1D53294AF7BC970221ECF3ACC8A7389918EDDCB59F00C700A3C15CEF3B43843CC79014F4E900CABF94CA9704DA56A0A51496A456D25A732F5F34F7D5CA5783BA8A4DB7877A82A19AF462F306F8A9F13DE1A9E3A4CC7E7C53BC58993E73DF29962D689CCD8A39AC86CC023C53B13789260DF3533BE216FFA69510DC3B805C0A92C54B949158DDCD418AF93DA1B799945CE99FF43C7E69BB7B891BE847F6681E5FD4AE5591BF2A42B32D435E3574347B193BA7DD439F3ADCB8332E6D52A6F583DB17E93F067F6F961FFCB64A9EA931DCE860A63EB70C6E23EF53DEC250DF1E380E7BF35C942C64F7CAFA8BF2DB77A67F57765E78FF829FE1DEB744DD73F595F5837EEC6]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Robot">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED977F6C545516C71FD3D21D89266B97342D742DA54442935742CA4E53A0A5A98C364B22EA1F6C79B89316FE6884A5DD140CE187ED542CAB16127E2C06636032D667C55F814D37B260B78602DBDAC6AC634424B54E82C82A14371A6C48DABC9DEF7CFAD2E7F26395B8D9DDEC3CC27039F7DC73BEE77BCE3DF7DEA274C308F80CC35FDDDD3DABB8E09DA387FFDCFD93EAC515B38ADB2B8E1E2EAF28EA8BFAABEA7776047C854F6D7A39E09BB671517FD43FF3774E73C077D70AA7D930E2E1DCF5015F6025E38E95015F7C5763B5F4C767D7057C17DB0756057CE75E4012A90BF8D6D7B2EAFC1ECD3EBD3DE09BF30AB3A5D1802F7B2DE39AFDD2647C3A24396B91E065FB0AA142B27193ECE0119B4898AD7E49D88A0F4C20212EE45E0963E2F56238BF238979C744BCF0804D6271E551FF8C5F10E3D6BAA87F61299AD8BC3E52F8813D24C445D4CFBE15F5CFEDDC702492B977F04A7BD41FEE691C886436FD85DF82632579AF5F6676E8E392BCCF3E98FD6924F3C4ABC7AF95E4196D835F95E4FDFC0F3B2F46329F78E6FD80B2096F3DF39547BC807CDD8EA87F7F3FBCE1256D4E24F3DC03AC0A4E8F6456D50D5444FD8DBF62EDD919F2D2982DCB9F2C88FA1F8C6CDBAF48610C96426F46FD39416A0654334F49133C5DD148E67DF7A0C3DA587724F3F9A2D133F24BED1577CBE3B1CFE4FDB93B4AF2DE5D052AF074E594E47D54861C244B3214F538DA24AB773FAA581EAF1287D406F92246628761A2D8592C56898E559521E18C3E2C9C66851092D36BF34BF266CF6016F9F2EA92BC6FEE0403160A7E2AFEC18624FF5EE50839BEB04CAE61983AC70B6374A828EA046C602652328B841CA149D47BD72BBFB0C1BE80E7C7BE157B305CF97949DE99B28C7EC5C52C19414EEEA83A1823A7FC527BCC527564012430402D1DEA104B58A606F0E83497E42DCB40E23447320F64A3B974B9698E66300BC34EB3691AC6E6A9A6B97A0159A01272E799A6391DCB540ED5452C444D9DC386DB79B45BDD7DAD7D4A3582C11D4B076C4D69A65956EBAD3D902027FB86619AAD6DCC521B6496EA259B2047823534594B85D44E314DBB139B609EB63B9279F4E2857AFD12CBB4DF97E49DBE86647C76AD24ACA5DE5AF24DB3F02C36610926C100C34395A659FA35FA83417515560DAE314DC741826534E99654055D949D4EA4F416324E6D53B16D0FAA5351F9542363ECE37D5C27D925A84FBA1955CA2AE2451366C817BB865D49D6DC9AD1AC2B572D110B16B006063A034CC230DD955AA26FD3E5A831626747D05BE084597A3EABE869440157E3276C32523A397EF14866872A2DABF46B7201F32DF9965578965DC01E21A2CE43CA63EE3CCB32A7935372BD74B965B9726A75F354CB5ABD80597677CBA8AA17B65B2F4987557871F7A06519467E97F014EC5355E477094FC13ECB721C3AC0C1ABAABAA6A164E524FB00B9C3BEE3A8B6F1C2AEC71718CEBF273C4D438A0ECBD8C123D6B080172CBB1EB50AFD83572DCBEEC472CBA86595D5E2BDF592A2C60B6374CEBF27663A0F295E66611239D1B10B887A708D244658BF70823E5C6107093192232CB3963C6293FC1A8665B5B6B1676BA708FF781F487AA456A9075691417A4E539A6224B32ECFB20657300033D418991A6738B9165F60C026B378641548C0463552876076F3A259320206583AB6DBB2823D1A1F38059F8B5FB4ACBAB7C96CC361CBDA72E491E39655DE5E78C2B25EDB43364FF458D6B6EDCD272D2B71FB4AE682ACE185B8F0426D7F586659590DD46DD9A284B52DE1855AFBC612CB7A32B46B9965DDFFD03BBFB61250C85AB0DEB2B6960BD5AC45644D38B7960B9B1514AAFB1F12AA27434295D540EC2E63F24B16E0813C0AC36B7BE4B1BC9D5CCBEF9623F25BF7B63C067B9EDEDE3736E71523EC38BAB54E8C1BABFBC60A9FEA58D93716DF8564FB8ABEB1691B912361766055DFD8B9179030CE5DD73776B11D8911EE1BBB6B45EEFABEB1C0CAEA9726AC151F90E6D91935C3275E5D92D11B3712A74C6F5CF7939AE1279ED9B33E34B2BF1F3B773F1A1AD13D41D86AF6F78DE946276B5EFBDBF6874616961AE1D0884E4FCDBA3A9A7535F57B3AD43796BD9658DA1EAC19DE3BF8785568646EE7CEE29A619D02F2B5B3433116F58546AAEA37BD2CCD45FDA11175484946CFD40C9F7BA0B83B34A2DB60CD70551DB160FFFC1E6166556954BEBCDEE12152A7280683BDF1D72FE337FFDEDEB8CE1AE199FDA938C1E3CC53A111DD51C5C9B8852427B0014EE45E6EB14FA4608037665905422F66C6D403597E3FA0787BE60B098C3DF66D6FFCDD55E0397E4D5983810BF535C33AB314D185B5BD719D658A0539AC2221B39D876231F534DB567FB36D8D633173FAB5F9BD71DD636B86EFBB6779756FFC9B3BCD8A9AE1E78B5A2FC562AB1764F427673FEF8D7F5486FE6F76C81AF8417EA55DBEA8A2E7EE105AAA2B385D38C10CC3835FA9DE88E5E0D558CCEE1CFA5859681A8AC5745E68769CFF2463304C5E400EAAD64BB6ADBE2D3C6973541B44D195D31B3F53067E986416240315AA1FB2402D91296A8CD88D702CA6F34E5C19E1DEF8B28C826362B8714035B3E188F80433968917FB7867477445C5A711AE19D61D929DA2F1A10EBCC8B2AB238FC8C991D76FC1BE584CE76C2CA66EAC2C53B7EC62F62FF54C16D81764E7D9B7B4CB881A4EC01C7D38349213240AF24274F0BFB56E022D554144E8901D2CB3160E436FCA26350FB7D376AB0EA93D6A92CA81432C8C578E875B6224762C907D6A9E4C513FEC1198A7F6C838B594DF65DBBA3FD8B6C6628F7D4427C11A55CD4EA18BD297D8EFB0FAC902C50BDBCCBA9C28476ECD882B7440051ED612110CB04F898B5F18F032C3986E037BEC3218A666A8A858B7720AE748E8C05E4DF62FD5853EFD909CBAB956C7A62A888E6A61B66534162BAB65173B4E2CA6FB9B982483EC50AA8EDA20FBF802099CF30B1E66E11F9BD41E3D015FD427B923A7540578580B127A021D18CCC442A4C44EAF607F8181ECD075C91A672B3D1C0BEC1D2C937D74584BCDD0A5D1A402B1D094269C8661DBBA5929C6A634DBD63D4D6819A3B379AABA197B19CDDA29E2812ECAB8255F95DC922F1E6AA7D8B6EE3FB2007B8E232F30C318260F5E95263AB9F3C400BEC0903B4F1D15860BF6D9B6FA8C760D3D8793058F4395D2A1F3B80865196CC448F72322BA225EE8CC8CD101C9E01A79C4F250A5FC0EAE915FF4E9C068B2CBF0EEB291AC872463B0E4B22D6C78E78C8013B8A24ED883F8C523911235B3C8C77B116C24D1C212B3605BBA5C27294C8299584048D6F0E59E29B6ADF345ABE8FCFC7222334B95923B2A9F3E464EF1F561996D67358417DA763C8CB53796D8B66EB9B6AD5BAE6D5B41AC05EB6D7B6BF95F37D876A44C12DD75A57F6CB7E48B5F9466C361ADE2847DE4B8EC149E90FD5F267EFDBF6D3E292F542C7927E3D4921BA3A2FE6302D59F760B9BEEC0B6AD3BB018132ADD816D5B7760DB0EF6C0A4F01C3835B7CDB6679E149E608FF0E85D206B42A57781EC088FDE05F24566E1C7DD05C2E3BE20A4E9AED26D1C39D6DCB78666C1E3BD9F83D3C5AF5544C1ADDE8D4E6374785F9005974FBD1DDC7784B8E5FDE26592D7040C83D095CBA6F7D50366DE4A20871FF28817DE2F78775F2BAEE5AC06B2E966592F1AF775A38CBB55341105EF265E526487370B6F377247A690B82FBB893CA2E37DEF4CE4DD45CBCBCB5B93F04C2EC802FCBBAF304541742077EB5CB1B82F3BC5083FA08237D0A2CF4BD0DD2913ABC0833570A2030FB0C45AC39864F8127F1D679291664C4EFCFD77FFB9B52FEF6C6A9C1AA7C6FF8B639F415FC9F88F23B9D9D8488ED292388D640704F324CFAF61A4DF50EECE4E3626224DFFA7D835722D785766783C2249BB4EA2DF61E7EF8E614CF548BE74B2123A39DFF122FB979CCB09CDACE4FFAF38E9897F7F96F8E3C5997683283E48AC39E3C8E75CA3C8B8BDEE7D7D27BF596FFF3E79F9A1BE6E0F676A9C1AA7C6FFCF636FE7FF3EDDFE87CABDA7DE84AFBF25FAED174E4E52E70B47DD3E3B31CA36BE7B3AA4DFF02CB8B99CDFC9B758255F5F3AA389DF512737F1FF5C63E2ECF025B1DD7EB7BCF5294FECE91EFBD74B7E5C8FFFEAD6012F939379FB312BCA497DA92FF5A5BED497FA525FEA4B7DA92FF5A5BED497FA525FEAFB2FFDFE01AF7AEE8B]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Axis">
          <Producers>
            <MeshBox Comment="R"/>
            <MeshTransform Scale="2 0.01 0.01" Position="2 0 0"/>
            <MeshExpression VertexColors="255">
              <Expression>
<![CDATA[C.R = 1;
C.G = 0;
C.B = 0;]]>
              </Expression>
            </MeshExpression>
            <MeshBox Comment="G"/>
            <MeshTransform Scale="0.01 2 0.01" Position="0 2 0"/>
            <MeshExpression VertexColors="255">
              <Expression>
<![CDATA[C.R = 0;
C.G = 1;
C.B = 0;]]>
              </Expression>
            </MeshExpression>
            <MeshCombine/>
            <MeshBox Comment="B"/>
            <MeshTransform Scale="0.01 0.01 2" Position="0 0 2"/>
            <MeshExpression VertexColors="255">
              <Expression>
<![CDATA[C.R = 0;
C.G = 0;
C.B = 1;]]>
              </Expression>
            </MeshExpression>
            <MeshCombine/>
          </Producers>
        </Mesh>
      </Children>
    </Group>
    <Group Comment="Collision Meshes">
      <Children>
        <Mesh Name="Mesh_Col_Cone">
          <Producers>
            <MeshSphere Scale="0.5 0.5 0.5" ZSamples="3" RadialSamples="18"/>
            <MeshTransform Rotation="0.25 0 0"/>
            <MeshExpression Expression="v.Y = v.Y &lt; 0.25 ? -0.5 : 0.5;"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Sphere">
          <Producers>
            <MeshBox Scale="1 0.5 1" XCount="18" YCount="12" Grid2DOnly="255"/>
            <MeshExpression Scale="0.5 0.5 0.5" AutoNormals="0">
              <Expression>
<![CDATA[//

        float E, A, K, X, Y, Z;

        // Convert range to radians

        E = v.Y*PI; // Elevation
        A = v.X*PI; // Azimuth

        // Convert spherical coordinates into cartesian

        K = cos(E);

        X = sin(A)*K;
        Y = sin(E);
        Z = cos(A)*K;

        // Assign coordinates

        v.X = X;
        v.Y = Y;
        v.Z = Z;

        n.X = X;
        n.Y = Y;
        n.Z = Z;]]>
              </Expression>
            </MeshExpression>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Box">
          <Producers>
            <MeshBox Scale="0.5 0.5 0.5"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Cylinder">
          <Producers>
            <MeshSphere ZSamples="6" RadialSamples="14"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[//V : current vertex
//VarP is set to the number of Z samples;
//VarQ is the number of circonferences wanted in the upper and lower faces
float VarP;
int VarQ = 1;
float Psi;

VarP = 6;//OrigSphere.ZSamples;
if (v.Y != 0 || v.X != 0) {
    Psi = atan2(v.Y,v.X);
    v.X = cos(Psi); //*pow(1-abs(v.Z),2);
    v.Y = sin(Psi);
    //Normals
    //n.X = cos(Psi);
    //n.Y = sin(Psi);

    if (VarQ == 0) {
        v.Z /= 1 - 2/(VarP-1);
        }
    else { //VarQ != 0
        v.Z /= 1 - (VarQ+1)*2/(VarP-1);
        if (abs(v.Z)>1.001) {   //this is because we are using floating point precision
                                     //it is better to avoid stuff like "Z > 1" due to truncament errors

            //remember: tallest's high: (1/(1 - (VarQ+1)*2/(VarP-1));
            v.X *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);
            v.Y *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);

            v.Z = v.Z/(abs(v.Z));

        }
    } //ELSE

    //Normals
    if (abs(v.Z) < 0.999) {
        n.Z = 0; //lateral surface
        }
    else {
        if (pow(v.X,2) + pow(v.Y,2) < 0.999) {
               n.X = 0;  //Upper surface
               n.Y = 0;
               }
        else {
                 //Edges normals, you can decide what to use.
        }
    }  // End of normals section

    //soft edges      Work In Progress
    /*
    if (abs(v.Z) > 0.7 && pow(v.X,2) + pow(v.Y,2) > 0.7) {
        v.Z *= 0.9;
        v.Y *= 0.9;
        v.X *= 0.9;
        }
    // */

} //OUTER IF]]>
              </Expression>
            </MeshExpression>
            <MeshTransform Scale="0.5 0.5 0.5" Rotation="0.25 0 0"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Capsule">
          <Producers>
            <MeshBox Scale="0.5 0.5 1" XCount="15" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[float RX, SX, CX;

RX = v.X*PI*2;

SX = sin(RX); CX = cos(RX);

v.X = SX/2;
v.Z = CX/2;

n.X = v.X;
n.Y = 0;
n.Z = v.Z;]]>
              </Expression>
            </MeshExpression>
            <MeshBox Scale="0.5 0.5 1" XCount="15" YCount="3" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[float RX, SX, CX,
      RY, SY, CY;

RX = v.X*PI*2;
RY = v.Y*PI/2-PI/4;

SX = sin(RX); CX = cos(RX);
SY = sin(RY); CY = cos(RY);

v.X = SX*CY/2;
v.Y = SY/2;
v.Z = CX*CY/2;

n.X = v.X;
n.Y = v.Y;
n.Z = v.Z;

v.Y -= 0.5;]]>
              </Expression>
            </MeshExpression>
            <MeshCombine/>
            <MeshBox Scale="0.5 0.5 1" XCount="15" YCount="3" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[float RX, SX, CX,
      RY, SY, CY;

RX = v.X*PI*2;
RY = v.Y*PI/2+PI/4;

SX = sin(RX); CX = cos(RX);
SY = sin(RY); CY = cos(RY);

v.X = SX*CY/2;
v.Y = SY/2;
v.Z = CX*CY/2;

n.X = v.X;
n.Y = v.Y;
n.Z = v.Z;

v.Y += 0.5;]]>
              </Expression>
            </MeshExpression>
            <MeshCombine/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_CheckSizes">
          <Producers>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Scale="2 1 1" Position="-3 0 0" Rotation="2 0 0"/>
            <MeshLoad Mesh="Mesh_Col_Sphere"/>
            <MeshTransform Position="-1.5 0 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Cone"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Cylinder"/>
            <MeshTransform Position="1.5 0 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Capsule"/>
            <MeshTransform Position="3 0 0"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Pyramid">
          <Producers>
            <MeshLoad Mesh="Mesh_Col_Cone"/>
            <MeshTransform Scale="1.52 1 1.52" Position="0 3.5 0" Rotation="0 0.875 0"/>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Scale="2.12 1 2.12" Position="0 2.5 0" Rotation="0 0.875 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Scale="3.18 1 3.18" Position="0 1.5 0" Rotation="0 0.875 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Scale="4.24 1 4.24" Position="0 0.5 0" Rotation="0 0.875 0"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Col_Plane">
          <Producers>
            <MeshLoad Mesh="Mesh_Col_Sphere"/>
            <MeshTransform Scale="1.33 1 4.41" Position="0 -0.1 -0.24" Rotation="0.98 0 0"/>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Scale="0.6 0.43 2.96" Position="-0.98 -0.17 1.01" Rotation="0 0.915 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="Mesh_Col_Box"/>
            <MeshTransform Scale="0.6 0.43 2.96" Position="0.98 -0.17 1.01" Rotation="0 0.088 0"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
      </Children>
    </Group>
  </Content>
</ZApplication>
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

And it's done!

The whole thing can be optimized, but it's working and already saving me tons of time.

Here's the latest ZGE project. I moved the creation of the ZGEBullet shapes to the same ZLibrary as the declaration of their names, so it is simpler to not forget things.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FixedFrameRate="60" ScreenMode="0" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[//

if (ANDROID)
{
  if(App.GLBase==0) this.ModuleName="libGLESv1_CM.so";
  else this.ModuleName="libGLESv2.so";
}
else if (LINUX)
{
  this.ModuleName = "libGL.so";
}
else
{
  this.ModuleName = "opengl32";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[const int GL_DEPTH_BUFFER_BIT = 0x0100;

void glClear(int mode){}


/* Boolean */
const int GL_ZERO = 0;
const int GL_ONE = 1;

/* BlendingFactorDest */
//const int GL_SRC_COLOR = 0x0300;
const int GL_ONE_MINUS_SRC_COLOR = 0x0301;
const int GL_SRC_ALPHA = 0x0302;
const int GL_ONE_MINUS_SRC_ALPHA = 0x0303;
//const int GL_DST_ALPHA = 0x0304;
//const int GL_ONE_MINUS_DST_ALPHA = 0x0305;

/* BlendingFactorSrc */
//const int GL_DST_COLOR = 0x0306;
const int GL_ONE_MINUS_DST_COLOR = 0x0307;
//const int GL_SRC_ALPHA_SATURATE = 0x0308;

/* EnableCap */
const int GL_BLEND = 0x0BE2;
//const int GL_COLOR_LOGIC_OP = 0x0BF2;

/* LogicOp */
//const int GL_INVERT = 0x150A;

void glDisable(int cap){}
void glEnable(int cap){}

//void glLogicOp(int opcode){}
//void glColor3f(float red, float green, float blue){}

void glBlendFunc(int sfactor, int dfactor){}

// Pour le screenshot
void glReadPixels(int x, int y, int width, int height, int format, int type, xptr data){}

// Coloration des vaisseaux sur un Hit avec une Light
//void glLightfv(int light, int pname, xptr params){}

// VR
//void glClear(int mask){}
void glViewport(int X, int Y, int Width, int Height){}
void glGetIntegerv(int pname, xptr params){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExternalLibrary Comment="Bullet 3D physics" ModuleName="ZgeBullet_x64">
      <BeforeInitExp>
<![CDATA[//

if(ANDROID)
{
  this.ModuleName = "./libZgeBullet.so";
}
else if(LINUX)
{
  this.ModuleName = "./ZgeBullet.so";
}
else
{
  this.ModuleName = "ZgeBullet_x64";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[/*
  ZgeBullet Library, a wrapper for the Bullet Physics Library.
  http://bulletphysics.org

  Project home
  https://github.com/Rado-1/ZgeBullet

  Download Windows DLL and Android shared library from
  https://github.com/Rado-1/ZgeBullet/releases

  Copyright (c) 2012-2016 Radovan Cervenka

  Version: 2.4 (2016-09-07)
*/



// Functions


// World
xptr zbtCreateWorld() {}
void zbtDestroyWorld(xptr world) {}
void zbtSetCurrentWorld(xptr world) {}
void zbtSetWorldGravity(float x, float y, float z) {}
void zbtStepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep) {}


// Collision shapes
xptr zbtCreateBoxShape(float x, float y, float z) {}
xptr zbtCreateSphereShape(float radius) {}
xptr zbtCreateScalableSphereShape(float radius) {}
xptr zbtCreateConeShape(float radius, float height) {}
xptr zbtCreateCylinderShape(float radius, float height) {}
xptr zbtCreateCapsuleShape(float radius, float height) {}
xptr zbtCreateCompoundShape() {}
xptr zbtAddChildShape(xptr compoundShape, xptr childShape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtRemoveChildShape(xptr compoundShape, xptr childShape) {}
xptr zbtCreateConvexHullShape(xptr points, int numPoints) {}
xptr zbtCreateMultiSphereShape(xptr positions, xptr radii, int numSpheres) {}
xptr zbtCreateTriangleMeshShape(xptr triangles, int numTriangles, int meshType) {}
void zbtUpdateDeformableTriangleMesh(xptr triangleMeshShape) {}
void zbtSetShapeLocalScaling(xptr shape, float x, float y, float z) {}
void zbtDeleteAllShapes() {}


// Rigid bodies
xptr zbtCreateRigidBodyXYZ(float mass, xptr shape, float x, float y, float z, float rx, float ry, float rz) {}
xptr zbtCreateRigidBody(float mass, xptr shape, xptr position, xptr rotation) {}
void zbtDeleteRigidBody(xptr rigidBody) {}
void zbtSetSleepingThresholds(xptr rigidBody, float linear, float angular) {}


// Collision objects (in general)

void zbtSetPosition(xptr obj, xptr position) {}
void zbtSetRotation(xptr obj, xptr rotation) {}
void zbtSetPosRot(xptr obj, xptr position, xptr rotation) {}


// Raycasting
xptr zbtRayTest(float fromX, float fromY, float fromZ, float toX, float toY, float toZ) {}
xptr zbtRayTestFiltered(float fromX, float fromY, float fromZ, float toX, float toY, float toZ, int filterGroup, int filterMask) {}
void zbtGetRayTestHitPointXYZ(ref float outX, ref float outY, ref float outZ) {}
]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary Comment="Google Doc - DATA" HasInitializer="1">
      <Source>
<![CDATA[/*
  In the real project, I have a shitload of models.
  So everything is listed in a Google Sheet doc where I can check if they are listed in the database,
  set their names...and then export this list.
*/

const int
  M_PLANE=0,
  M_PYRAMID=1,
  M_TRUNK=2,
  M_BUILDING=3,
  M_ROBOT=4;

{
  DB_List.SizeDim1=5;
  DB_List[0]=M_PYRAMID;
  DB_List[1]=M_TRUNK;
  DB_List[2]=M_BUILDING;
  DB_List[3]=M_PLANE;
  DB_List[4]=M_ROBOT;
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="ZGEBullet Shapes">
      <Source>
<![CDATA[xptr World,

  SHAPE_BOX_2000x5000x4000,
  SHAPE_SPHERE_1000,
  SHAPE_SPHERE_1330x1000x4410,
  SHAPE_CAPSULE_3000x2530,
  SHAPE_CYLINDER_1000x3000,
  SHAPE_BOX_600x430x2960,
  SHAPE_BOX_2120x1000x2120,
  SHAPE_BOX_4240x1000x4240,
  SHAPE_BOX_3180x1000x3180,
  SHAPE_CONE_1500x1000,
  SHAPE_PLANE,
  SHAPE_PYRAMID;


void initBulletShapes()
{
  SHAPE_BOX_600x430x2960 = zbtCreateBoxShape(0.3, 0.215, 1.48);
  SHAPE_BOX_2000x5000x4000 = zbtCreateBoxShape(1.0, 2.5, 2.0);
  SHAPE_BOX_2120x1000x2120 = zbtCreateBoxShape(1.06, 0.5, 1.06);
  SHAPE_BOX_4240x1000x4240 = zbtCreateBoxShape(2.12, 0.5, 2.12);
  SHAPE_BOX_3180x1000x3180 = zbtCreateBoxShape(1.59, 0.5, 1.59);

  SHAPE_SPHERE_1000 = zbtCreateSphereShape(0.5);

  SHAPE_SPHERE_1330x1000x4410 = zbtCreateScalableSphereShape(1.0);
  zbtSetShapeLocalScaling(SHAPE_SPHERE_1330x1000x4410, 0.665, 0.5, 2.205);

  SHAPE_CONE_1500x1000 = zbtCreateConeShape(0.75, 1.0);

  SHAPE_CYLINDER_1000x3000 = zbtCreateCylinderShape(0.5, 1.5);

  SHAPE_CAPSULE_3000x2530 = zbtCreateCapsuleShape(1.5, 2.53);

  SHAPE_PLANE = zbtCreateCompoundShape();
  zbtAddChildShape(SHAPE_PLANE, SHAPE_BOX_600x430x2960, 0.98,-0.17,1.01, 0.0,0.088,0.0);
  zbtAddChildShape(SHAPE_PLANE, SHAPE_BOX_600x430x2960, -0.98,-0.17,1.01, 0.0,0.915,0.0);
  zbtAddChildShape(SHAPE_PLANE, SHAPE_SPHERE_1330x1000x4410, 0.0,-0.1,-0.24, 0.98,0.0,0.0);

  SHAPE_PYRAMID = zbtCreateCompoundShape();
  zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_2120x1000x2120, 0,2.5,0, 0,-0.125,0);
  zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_3180x1000x3180, 0,1.5,0, 0,-0.125,0);
  zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_4240x1000x4240, 0,0.5,0, 0,-0.125,0);
  zbtAddChildShape(SHAPE_PYRAMID, SHAPE_CONE_1500x1000, 0,3.5,-0.01, 0,0,0);
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Functions">
      <Source>
<![CDATA[
xptr CreateRigidBody(model object, int type)
{
  switch(type)
  {
    case M_PLANE:     return zbtCreateRigidBody(1, SHAPE_PLANE, object.Position, object.Rotation);
    case M_PYRAMID:   return zbtCreateRigidBody(1, SHAPE_PYRAMID, object.Position, object.Rotation);
    case M_TRUNK:     return zbtCreateRigidBody(1, SHAPE_CYLINDER_1000x3000, object.Position, object.Rotation);
    case M_BUILDING:  return zbtCreateRigidBody(1, SHAPE_BOX_2000x5000x4000, object.Position, object.Rotation);
    case M_ROBOT:     return zbtCreateRigidBody(1, SHAPE_CAPSULE_3000x2530, object.Position, object.Rotation);
  }

  // Got to return something, otherwise ZGEBullet will crash
  trace("\nThere is a problem with CreateRigidBody type=" + intToStr(type) + "\n");
  return zbtCreateRigidBody(1, SHAPE_SPHERE_1000, object.Position, object.Rotation);
}


void RenderMesh(int mesh)
{
  switch(mesh)
  {
    case M_PLANE:     @RenderMesh(Mesh:Mesh_Plane); return;
    case M_PYRAMID:   @RenderMesh(Mesh:Mesh_Pyramid); return;
    case M_TRUNK:     @RenderMesh(Mesh:Mesh_Trunk); return;
    case M_BUILDING:  @RenderMesh(Mesh:Mesh_Building); return;
    case M_ROBOT:     @RenderMesh(Mesh:Mesh_Robot); return;
  }
}



/*
  Set the visible collision Mesh, that has no collision at all...
*/

vec3 ScaleDatabaseCollisionMesh(int type)
{
  switch(type)
  {
    case M_TRUNK:     return vector3(1.0, 3.0, 1.0);
    case M_BUILDING:  return vector3(2.0, 5.0, 4.0);
    case M_ROBOT:     return vector3(3.0, 2.53, 3.0);
  }
  // Or return no scale
  return vector3(1.0, 1.0, 1.0);
}

void UpdateDatabaseCollisionMesh()
{
  int CurrentView = DB_List[MenuData];

  DatabaseTransformCollision.Scale = 1;
  DatabaseTransformCollision.Translate = 0;
  DatabaseTransformCollision.Rotate = 0;

  DatabaseTransform.Scale = 1;
  DatabaseTransform.Translate = 0;
  DatabaseTransform.Rotate = 0;

  ModelViewerBodyCenter = 0;

  switch (CurrentView)
  {
    case M_BUILDING:  DatabaseCollisionMesh.Mesh = MESH_SHAPE_BOX; break;
    case M_PLANE:     DatabaseCollisionMesh.Mesh = MESH_SHAPE_PLANE; break;
    case M_TRUNK:     DatabaseCollisionMesh.Mesh = MESH_SHAPE_CYLINDER; break;
    case M_PYRAMID:
      ModelViewerBodyCenter = vector3(0, -2.0, 0);
      DatabaseCollisionMesh.Mesh = MESH_SHAPE_PYRAMID;
      break;
    case M_ROBOT:     DatabaseCollisionMesh.Mesh = MESH_SHAPE_CAPSULE; break;
  }

  DatabaseTransform.Translate = ModelViewerBodyCenter;
  DatabaseTransformCollision.Scale = ScaleDatabaseCollisionMesh(CurrentView);

  // Set the Body, that has the real collision, but is not visible
  if (Database_Body != null) zbtDeleteRigidBody(Database_Body);
  Database_Body = CreateRigidBody(ModelViewer, CurrentView);

  vec3 ModelViewerPosition = ModelViewer.Position;
  ModelViewerPosition.X += ModelViewerBodyCenter.X;
  ModelViewerPosition.Y += ModelViewerBodyCenter.Y;
  ModelViewerPosition.Z += ModelViewerBodyCenter.Z;
  zbtSetPosRot(Database_Body, ModelViewerPosition, ModelViewer.Rotation);
}]]>
      </Source>
    </ZLibrary>
    <ZLibrary Comment="Math">
      <Source>
<![CDATA[const float CAM_FOV = 0.4142; // = tan(App.FOV(45) / 360 * PI);


vec3 CalculatePosition(vec3 vector, vec3 rotation)
{
  float X, Y, Z, SX, CX, SY, CY, SZ, CZ;
  X = rotation.X*PI*2;
  Y = rotation.Y*PI*2;
  Z = rotation.Z*PI*2;
  SX = sin(X); CX = cos(X);
  SY = sin(Y); CY = cos(Y);
  SZ = sin(Z); CZ = cos(Z);
  return vector3(vector.X * CY*CZ + vector.Y * (SX*SY*CZ-CX*SZ) + vector.Z * (CX*SY*CZ+SX*SZ),
                 vector.X * CY*SZ + vector.Y * (SX*SY*SZ+CX*CZ) + vector.Z * (CX*SY*SZ-SX*CZ),
                 vector.Y * SX*CY - vector.X * SY + vector.Z * CX*CY );
}]]>
      </Source>
    </ZLibrary>
    <ZExpression Comment="init physics">
      <Expression>
<![CDATA[// Init physical world

World = zbtCreateWorld();
zbtSetCurrentWorld(World);
zbtSetWorldGravity(0, 0, 0);

initBulletShapes();]]>
      </Expression>
    </ZExpression>
    <SpawnModel Model="ModelViewer" Position="0 0 -6" SpawnStyle="1"/>
    <ZExpression>
      <Expression>
<![CDATA[MenuData = 0;

Database_Body = null;

UpdateDatabaseCollisionMesh();

ModelViewer.RotationVelocity.X = rnd();
ModelViewer.RotationVelocity.Y = rnd();]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression Comment="Mouse raycast">
      <Expression>
<![CDATA[Axis[0,0] = 0;
Axis[1,0] = 0;

zbtStepSimulation(App.DeltaTime, 0, 0);

// Mouse Raycast

float x = App.MousePosition.X * CAM_FOV * App.ViewportWidth / App.ViewportHeight; // If you're using a constant aspectRatio, you can swap out "App.ViewportWidth/App.ViewportHeight" with a specific value.
float y = App.MousePosition.Y * CAM_FOV;

Transform_MouseToRay.Translate.X = x;
Transform_MouseToRay.Translate.Y = y;
Transform_MouseToRay.Translate.Z = App.CameraPosition.Z - 1;

float x2 = x * 950; // WTF, why 950?
float y2 = y * 950;

if (zbtRayTest(x, y, App.CameraPosition.Z, x2, y2, -1000) != null) // The body has been hit by the ray
{
  MouseToRaySize.Scale = 0.006;
  DatabaseCollisionColor.Color = vector4(1,0,0,0.6); // Red
  MouseToRayColor.Color = vector4(1,1,0,1); // Red
}
else
{
  MouseToRaySize.Scale = 0.005;
  DatabaseCollisionColor.Color = vector4(1,1,1,0.1); // White
  MouseToRayColor.Color = vector4(1,1,1,1); // White
}]]>
      </Expression>
    </ZExpression>
    <KeyPress Comment="Left" Keys="&lt;QA">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Right" Keys="&gt;D">
      <OnPressed>
        <ZExpression Expression="Axis[0,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Up" Keys="^ZW">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = 1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Down" Keys="_S">
      <OnPressed>
        <ZExpression Expression="Axis[1,0] = -1;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="Space" CharCode="32" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[MenuData += 1;
if(MenuData == DB_List.SizeDim1) MenuData = 0;

UpdateDatabaseCollisionMesh();]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <RenderTransformGroup Name="Transform_MouseToRay" Translate="-1.2634 0.4142 9">
      <Children>
        <UseMaterial Material="DatabaseRayMaterial"/>
        <RenderSetColor Name="MouseToRayColor" Color="1 1 1 1"/>
        <RenderTransformGroup Name="MouseToRaySize" Scale="0.005 0.005 0.005">
          <Children>
            <RenderMesh Mesh="MESH_SHAPE_SPHERE"/>
          </Children>
        </RenderTransformGroup>
      </Children>
    </RenderTransformGroup>
  </OnRender>
  <Content>
    <Model Name="ModelViewer" Position="0 0 -6" Rotation="0.2206 1.8588 0">
      <Definitions>
        <Variable Name="ModelViewerBodyCenter" Type="7"/>
      </Definitions>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Rotate the model with arrow keys or decelerate rotation if not

if(Axis[1,0] && abs(CurrentModel.RotationVelocity.X) < 0.4) CurrentModel.RotationVelocity.X -= 0.5 * Axis[1,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.X -= App.DeltaTime * CurrentModel.RotationVelocity.X;

if(Axis[0,0] && abs(CurrentModel.RotationVelocity.Y) < 0.4) CurrentModel.RotationVelocity.Y += 0.5 * Axis[0,0] * App.DeltaTime;
else CurrentModel.RotationVelocity.Y -= App.DeltaTime * CurrentModel.RotationVelocity.Y;


// Set the Collision Body position and rotation

vec3 ModelViewerPosition = CalculatePosition(ModelViewerBodyCenter, ModelViewer.Rotation);
ModelViewerPosition.X += ModelViewer.Position.X;
ModelViewerPosition.Y += ModelViewer.Position.Y;
ModelViewerPosition.Z += ModelViewer.Position.Z;
zbtSetPosRot(Database_Body, ModelViewerPosition, ModelViewer.Rotation);]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <UseMaterial Material="GhostMaterial"/>
        <RenderTransformGroup Name="DatabaseTransform" Translate="0 -2 0">
          <Children>
            <ZExpression Comment="Render Mesh">
              <Expression>
<![CDATA[// I do it that way, because I use the same RenderMesh function to render things within the game too

int mesh = DB_List[MenuData];
RenderMesh(mesh);]]>
              </Expression>
            </ZExpression>
            <RenderTransformGroup Name="DatabaseTransformCollision">
              <Children>
                <RenderSetColor Name="DatabaseCollisionColor" Color="1 1 1 0.1"/>
                <RenderMesh Name="DatabaseCollisionMesh"/>
              </Children>
            </RenderTransformGroup>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
    <Array Name="Axis" Dimensions="1" SizeDim1="2" SizeDim2="3"/>
    <Array Name="DB_List" Type="1" SizeDim1="5"/>
    <Variable Name="MenuData" Type="1"/>
    <Variable Name="Database_Body" Type="9"/>
    <Group Comment="Materials">
      <Children>
        <Material Name="GhostMaterial" Shading="1" Color="1 1 1 0.2" SpecularColor="0 0 0 1" EmissionColor="0 0 0 1" Blend="1" ZBuffer="0"/>
        <Material Name="DatabaseRayMaterial" Shading="1" Color="1 0 0.502 1" Light="0" SpecularColor="1 1 1 1" EmissionColor="1 0.502 1 1" Shininess="1"/>
      </Children>
    </Group>
    <Group Comment="Meshes">
      <Children>
        <Mesh Name="Mesh_Trunk">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED983F6B9CCB1587C7B65204EE1606072EB752A3E54A95903E40E63B683B23175B48626AB5C242DDE20F2009D248A8122AB7573E8084AA354881B8BC315C9C2E8108FC669EF3EC78951B1CECC4E63AB02FEC683C7FCEF99DDF3967E68CDF3E49E90F8F13DF654A7FBCF4EFFEEF53CA99BF9B0BE72B6F5EF49FD6F1BCB99012FDF39594DFBC385FA9FFAE6BEA5C3E5FE93F7DF322A5CD0567F79652DA5B72D7DED2F9CADE12230777CC76978C77978C1FDC7555DBEDBBC593F395D3FBC593944EEFBBCBF395DB770777EC62B6FF9491FED3833B64DA67E5E602BB36175CE38818D27ED893DD4B2B1E2588EAF6DDCCA2D37B5AB6742FBB8E9691C51366174F44CB7A306305721A72B4801FE4C8A83BABF6AE3BBD87B79934FAD5F69729D1AA578D32ACBDA212E7ED3B98945BF1CBE4D4AE9026CEA6E57C65F1C456B4CE22A7BB94AB2933FB55726A1C8AB9AEE944AE2F9A8DF8424B417870272A2D12AD966205B2B428A48734E264C64C6809C6F4BE7C62639515A8F4E034BA02AD31F3D017EA9ACA746FC86CBAD8DBEC45A61C1A397228DBCC6EF56F76CE5776978DDBF58DF395EDB5B3BAFE2CEB97EDB5F395F58DDDE5F3959B1D7DB7D5D78360607CF590355717E264EFF34BE41C5D6B23F25FBD46D7F1EDEA21BAE4F0EA025DCF2FD1258747D7E87AF51A5DFAF7F8562FA38BF1D543D65C5DE853F63EBF44CED1B5DE417ED595F51AAD9CE853F977AFFE354EDC5B5796C8E81C195D22A3736474898CCE913B3932BAC05ECAE1DF12FECD21BF1045757D72EF54E6BEFEA9B3E8CDA13787DEC2BFC94664463CE4888702F290197DD7A88B5D9129D9B59129C57E446C8988CD11B12522B62829223647C496C8C7121994238372F05C22834A64508E0CCA91412532E8832DDA58639C1324B8929F2933C121B3334EE00A0971BE1567234A4B9C6059097142963821739C9085D30699AE71AF566BA332D5A5EDCD3A2DC26A2DAD115BE224CFA28E93BC20158D8C447EE5C8AFC2E9474BF6444667FACA61AFD22AD31D7E1C4CE07930A1DF750777154B076F8C9CDE0F26F4590916ACA64DFB9C5BF0825DB4D5A68EDCA4E584C33EDACA50C7294DCB99C2294DCB38FEA2653DA7342D72E08D16F9C4032DDAE18A16EDAC45EFE2C960D2507597830923221C4CF035385D231E2CC2F63A9BA7A8B2F807136200FC2923478429551E2A5A7810BF32DFBC40A69CA84BE4FDA7F08634FAA0441A39C2ACE3DA85047C4CDF35DA28AAAE43AF68654C6922518B08E55C8BF0267D2C755C4EE4CA59A5E9D9A60539482636D06E54C83C12C8C7F048B554749C1B813F7C01C3ADEF1A3D6524C82D9823F62B2AB538A25E574EF90CFEF58B3C8B4199EA129B16AD1E0E26BBCBE8DDEACBC3D5C560C2F93C989C4D3D7E743D98703E0F269CCF587D7CDB18E67C1E4C389F0713CE67B8652FE7F360C2F90C1B68E12E184C8E6F8D28B14D232DCB33ADF6DEEC804A1BD737C07396C1A3BDDB6BE0D95D068F31B6D59F451AE3DC4DE0D1D7ECE56E028F5184FC57AF65C6F89199E617BC2086F58DDE787B4D0C373BBD3123E2E98DCFB2A85C232AC7B7D77A63FACC5E5DF4C647D732B07AD81BBF7A2D33CF2F7BE3C6926B66BC6DAFB1727759EF30BBBD26FFC7B7BD71F7F2D5EBDE983E562B536ED37E6FCC6D8B2E3DA296A3EBDE983EF2F5AC329BAFC1DC22A137DEEA1B0F6268FE45027A9BAFD5EE4843D556B66811A7F1A35E67D5E8B8DE81B1AB0BD8A38F7CDBDD65E43BAB67C5A62FF491981979F55A2FE391A36BBD6C5F2DCA778D5CB9C6BD72A896AD3E16A9DDE8B265E466A7CD62A3AD1E77D648B00668B727F7A637A6779937A6771CB3518F95A8C74AD46325EAB112F558897AAC443D56A21E2B518F4D6FB7A8C74AD46325EAB112F558897AAC443D56A21E2B518F95A8C74AD46325EAB112F558399B5608B5D62A518F95A8C74AD463A5D5098C473D56A21E2B518F95A8C74AD46325EAB112F558D4245635DED7B4D6065BFDE1080ED1A22DBBCBC3D1CDCEFAC670D46C3FCBC3D1D9940DE5AF1E0E47E411F2AF2E86237CCD382B9F7FB09A71A4E13B2583598D373BC311598C4C75C93C9289257C91F68723E29FF1C606D8E4E4E87A385ADF403B793ADB2B93C7B768912579632F8C0D47E4200865EFF92598B7D790A6BD7A59AB1927E6679ED522597A180F4A685EC076B9527ED38E84E63B24CB9892E54D8DDA2E27722527482367D16BB4C883E3DA2E27CE1A694820BF8CBDE1E8F856261B33E09731D7E83B7DEADE692CC5DE16BD8C5B0DD6F320833065EE2F6ABC6A410643CA67D3EAB7E67AC6832913CFD47555526E3530E370C24EAB5FF6C264CAC433D5005AC09032F797157E549356C5D91AB555B3DC5FA0B2E65CDF004FE5239F4DEBCFED35462BEA4C2E83A446DC876A93713C0E1EEB52F612D5E0B15A463EF757BCE7B3F53035597B8F70FF6BAF2F29DF565AAD7C5F73BEE3D4EEDB4D5DED35874699F1CDE52B4C7EDAAB0D2DCA9767DF7DEE55A668D5EE6BAEA10289FEF285A837DB0B11C6EC379FF266943DDFAABE52E556CE7DC9CAB92FDCE65F90B7B72416F97ED4B3BE1FF5F8F42D19BE6EAF57D6E891872FDFC6A4ACC24063154E64D295F2A904ED923D2D92376358CCDAE23B5D690FDFE332D0E20A0932A0ED6296F3F606C7969629C854979CB7D734B6CB89FEBD7D57EFB8FAAE198E78E3D40CADFDDE98F7726FCCBD46CB1D4A04F7C6C43FEDDE526FCCBD463BBB97C985BA725A2959236D2EF4C6E4C2ECDE4C895BF5E1CDCB9AC5136F7F24741F6E70EE7474910BDEDD563EE802799A5638561AA7F7E86A9509BABA0E2DB36A6AF590358B27D639DADE2A347469FBB4D6AA18B8D76861837B8D160CBC338623F2D1F1E1887ADB95F4DD9B326BDC6B5F5BF69660DB35E291F9D3FBE188FECC23CA5497D8D425064E4BDE11C8673DE7A42739673B32E1C7B39AD31BF9F0C379EEF9DC759CC99ECC6A7F788B89CAFBC51B47549ECC9ED5E0E19CA4658438198E88135A2CE59C0455FF2978DABD0F9ECD05F078AF79DFA504126F70663927C1E39D850462033CDE2032E90D224BF22C4B535F044BC698F2956C1C1A6F8E28DF957ACA11B5CC56F20624D2CC0B6D375F64A0654DB0116B64C97C9119F348AFD9778D7B5D633CEB237D673C9B65722BABE6A319EA781B41CE145BF45D33F317AF4CE2A771455C99596A348A44227E4744EE4A51193F0D2D1CCA9208E54DE4293D4A8FEBAFEB1EA527E937F5F765FBFC87FFA3FA5B48B3FEE34FECD3439A7F1D7FF21FD63C94F0B86AFCD2B67C5EFFAE03C3F21790A9D5FF6EE3A37F6166E117E30FB9FA187B9FBEFE5DF7A4F69EA59FBB9F2BA267E9771FE5FF631EF9D4F57FED52FDBDAFEDFBEE591D79F68DFAF7DBEEC3CFFB8EFEC22771F5B118FBDAFDFF173FFEB9F278D72DA53F753F56CC3F7E555D0F7DF13572F921E7BF3C9F7FAA79F797EE71C8FCBEB6DF7F76FE7EEEFAB755E3DB69BEFF50477E98E7FBFCBEF88CF899DF17F3F3677EFE7CABFDAF71167D6E04FE2F67D1E745D7AF7716CD73F6BFE3F91FB5AEFB5BF7DBF4F7EEBBBAFEBB5F3D5F3ED6EFE6DFFC9B7FF36FFECDBFF937FFE6DFFC9B7FF36FFECDBFF9F74D7CFF0421898BCC]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Pyramid">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED51B191C2400C5CFCF01145B8063C43EC329E8C8836B836889CD1865D80A981223EFCF9172BE974F6CC270C0C19E739495E492B9DF405600B3DED4031B81E5A20B5A693392945DC329D4C27ECA5A775945E3A95D83BE25E39A2DD7CA306592E6B958AC89598DAD7402E6B95A895E17056D4F024FD6917FCC48D7942A43B9CCDEE02A1CF7AD0DC71749EA699D888A9DD07D2342AD95146B48A476666EB2A6745B7252BC794DCD3AED46296BF7A8A274B3D4D204716CE7174DC6BC51BB54A4C43F963AA36EDF26ACEDCF12E7AE6AC6D533E73DF60DE85EFD2B639F7E68DD866F3468A4D9FC57B0CB040C52BB2C00756BCF3CFF10A1A052C29FFA4C2AFACF0C30B7CF21FBC4274C9BF39CF63B9CFD47ADEFEFFF67B72E57D5E7A6E7B3B92D7]]>
              </MeshData>
            </MeshImport>
            <MeshTransform Position="0 2 0"/>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Plane">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CDD4FB10DC23010BC3801092A4449C1082051D1E10118222BB081178884C400D904D2D1241D4C105150D222C8F3FE77424A6ADEF2CB3EDF9DEF570066902AAAB23C02930270B62AB71B606FE90497D5704056F3D9F79C724652E0BCF3322258E128224C46A02A45B8B8D76BE632BFF5E939A4E26983D675BDF50F6EDE87E87ED00CC141F080485AC5434E55A9BF6A95A319348F765575F37A95F8A7BD79C3A4FAFBD7337010C1F0268A1063C05B972206FE1D481005DCA0A117BD29E6F7863334D2877C7B92C118233CC86BA7CC4C3AB7DFD78D2E74A5399658C894FF501F4EDF7856]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Building">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED53BB4EC340105C830DE1D9A21408241A1054F448EE43CF1FA4A2E0135CF305B8434AFE800F081D4D6A40502252D20404029265E7C6CB3914D052E424AFC7B30FEFCDDE7544E458B08A9EC8E6A5013CB9C8B93D45AE2AD57284B77E2FE093B3FD0BD87E5FB5DBA205E371867BF6559425B014AAFD436029C4EA2FDC8099AC89AFFE21BDC82577DDD19EF771DDB16C3938855D3CAAAC757CB355D9C2FB3E38451656B765F113FBE8B6AC8E9C9CD1D21B7BF1D8D857D9C47FCB266A02C3A287F6807D62A7ED01F70B063CBCDCB57BC930DEBDAC409E5965133B2A9BD84BC045C039F9FAAEDD1B156024BDED019587E6ED812B5F96AC5C578C5988F12CC6034B8EF9EE0D31D9BD21260E0C26AA77B50C0C0B25A12DF1D53294AF7BC970221ECF3ACC8A7389918EDDCB59F00C700A3C15CEF3B43843CC79014F4E900CABF94CA9704DA56A0A51496A456D25A732F5F34F7D5CA5783BA8A4DB7877A82A19AF462F306F8A9F13DE1A9E3A4CC7E7C53BC58993E73DF29962D689CCD8A39AC86CC023C53B13789260DF3533BE216FFA69510DC3B805C0A92C54B949158DDCD418AF93DA1B799945CE99FF43C7E69BB7B891BE847F6681E5FD4AE5591BF2A42B32D435E3574347B193BA7DD439F3ADCB8332E6D52A6F583DB17E93F067F6F961FFCB64A9EA931DCE860A63EB70C6E23EF53DEC250DF1E380E7BF35C942C64F7CAFA8BF2DB77A67F57765E78FF829FE1DEB744DD73F595F5837EEC6]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_Robot">
          <Producers>
            <MeshImport HasVertexColors="1">
              <MeshData>
<![CDATA[789CED977F6C545516C71FD3D21D89266B97342D742DA54442935742CA4E53A0A5A98C364B22EA1F6C79B89316FE6884A5DD140CE187ED542CAB16127E2C06636032D667C55F814D37B260B78602DBDAC6AC634424B54E82C82A14371A6C48DABC9DEF7CFAD2E7F26395B8D9DDEC3CC27039F7DC73BEE77BCE3DF7DEA274C308F80CC35FDDDD3DABB8E09DA387FFDCFD93EAC515B38ADB2B8E1E2EAF28EA8BFAABEA7776047C854F6D7A39E09BB671517FD43FF3774E73C077D70AA7D930E2E1DCF5015F6025E38E95015F7C5763B5F4C767D7057C17DB0756057CE75E4012A90BF8D6D7B2EAFC1ECD3EBD3DE09BF30AB3A5D1802F7B2DE39AFDD2647C3A24396B91E065FB0AA142B27193ECE0119B4898AD7E49D88A0F4C20212EE45E0963E2F56238BF238979C744BCF0804D6271E551FF8C5F10E3D6BAA87F61299AD8BC3E52F8813D24C445D4CFBE15F5CFEDDC702492B977F04A7BD41FEE691C886436FD85DF82632579AF5F6676E8E392BCCF3E98FD6924F3C4ABC7AF95E4196D835F95E4FDFC0F3B2F46329F78E6FD80B2096F3DF39547BC807CDD8EA87F7F3FBCE1256D4E24F3DC03AC0A4E8F6456D50D5444FD8DBF62EDD919F2D2982DCB9F2C88FA1F8C6CDBAF48610C96426F46FD39416A0654334F49133C5DD148E67DF7A0C3DA587724F3F9A2D133F24BED1577CBE3B1CFE4FDB93B4AF2DE5D052AF074E594E47D54861C244B3214F538DA24AB773FAA581EAF1287D406F92246628761A2D8592C56898E559521E18C3E2C9C66851092D36BF34BF266CF6016F9F2EA92BC6FEE0403160A7E2AFEC18624FF5EE50839BEB04CAE61983AC70B6374A828EA046C602652328B841CA149D47BD72BBFB0C1BE80E7C7BE157B305CF97949DE99B28C7EC5C52C19414EEEA83A1823A7FC527BCC527564012430402D1DEA104B58A606F0E83497E42DCB40E23447320F64A3B974B9698E66300BC34EB3691AC6E6A9A6B97A0159A01272E799A6391DCB540ED5452C444D9DC386DB79B45BDD7DAD7D4A3582C11D4B076C4D69A65956EBAD3D902027FB86619AAD6DCC521B6496EA259B2047823534594B85D44E314DBB139B609EB63B9279F4E2857AFD12CBB4DF97E49DBE86647C76AD24ACA5DE5AF24DB3F02C36610926C100C34395A659FA35FA83417515560DAE314DC741826534E99654055D949D4EA4F416324E6D53B16D0FAA5351F9542363ECE37D5C27D925A84FBA1955CA2AE2451366C817BB865D49D6DC9AD1AC2B572D110B16B006063A034CC230DD955AA26FD3E5A831626747D05BE084597A3EABE869440157E3276C32523A397EF14866872A2DABF46B7201F32DF9965578965DC01E21A2CE43CA63EE3CCB32A7935372BD74B965B9726A75F354CB5ABD80597677CBA8AA17B65B2F4987557871F7A06519467E97F014EC5355E477094FC13ECB721C3AC0C1ABAABAA6A164E524FB00B9C3BEE3A8B6F1C2AEC71718CEBF273C4D438A0ECBD8C123D6B080172CBB1EB50AFD83572DCBEEC472CBA86595D5E2BDF592A2C60B6374CEBF27663A0F295E66611239D1B10B887A708D244658BF70823E5C6107093192232CB3963C6293FC1A8665B5B6B1676BA708FF781F487AA456A9075691417A4E539A6224B32ECFB20657300033D418991A6738B9165F60C026B378641548C0463552876076F3A259320206583AB6DBB2823D1A1F38059F8B5FB4ACBAB7C96CC361CBDA72E491E39655DE5E78C2B25EDB43364FF458D6B6EDCD272D2B71FB4AE682ACE185B8F0426D7F586659590DD46DD9A284B52DE1855AFBC612CB7A32B46B9965DDFFD03BBFB61250C85AB0DEB2B6960BD5AC45644D38B7960B9B1514AAFB1F12AA27434295D540EC2E63F24B16E0813C0AC36B7BE4B1BC9D5CCBEF9623F25BF7B63C067B9EDEDE3736E71523EC38BAB54E8C1BABFBC60A9FEA58D93716DF8564FB8ABEB1691B912361766055DFD8B9179030CE5DD73776B11D8911EE1BBB6B45EEFABEB1C0CAEA9726AC151F90E6D91935C3275E5D92D11B3712A74C6F5CF7939AE1279ED9B33E34B2BF1F3B773F1A1AD13D41D86AF6F78DE946276B5EFBDBF6874616961AE1D0884E4FCDBA3A9A7535F57B3AD43796BD9658DA1EAC19DE3BF8785568646EE7CEE29A619D02F2B5B3433116F58546AAEA37BD2CCD45FDA11175484946CFD40C9F7BA0B83B34A2DB60CD70551DB160FFFC1E6166556954BEBCDEE12152A7280683BDF1D72FE337FFDEDEB8CE1AE199FDA938C1E3CC53A111DD51C5C9B8852427B0014EE45E6EB14FA4608037665905422F66C6D403597E3FA0787BE60B098C3DF66D6FFCDD55E0397E4D5983810BF535C33AB314D185B5BD719D658A0539AC2221B39D876231F534DB567FB36D8D633173FAB5F9BD71DD636B86EFBB6779756FFC9B3BCD8A9AE1E78B5A2FC562AB1764F427673FEF8D7F5486FE6F76C81AF8417EA55DBEA8A2E7EE105AAA2B385D38C10CC3835FA9DE88E5E0D558CCEE1CFA5859681A8AC5745E68769CFF2463304C5E400EAAD64BB6ADBE2D3C6973541B44D195D31B3F53067E986416240315AA1FB2402D91296A8CD88D702CA6F34E5C19E1DEF8B28C826362B8714035B3E188F80433968917FB7867477445C5A711AE19D61D929DA2F1A10EBCC8B2AB238FC8C991D76FC1BE584CE76C2CA66EAC2C53B7EC62F62FF54C16D81764E7D9B7B4CB881A4EC01C7D38349213240AF24274F0BFB56E022D554144E8901D2CB3160E436FCA26350FB7D376AB0EA93D6A92CA81432C8C578E875B6224762C907D6A9E4C513FEC1198A7F6C838B594DF65DBBA3FD8B6C6628F7D4427C11A55CD4EA18BD297D8EFB0FAC902C50BDBCCBA9C28476ECD882B7440051ED612110CB04F898B5F18F032C3986E037BEC3218A666A8A858B7720AE748E8C05E4DF62FD5853EFD909CBAB956C7A62A888E6A61B66534162BAB65173B4E2CA6FB9B982483EC50AA8EDA20FBF802099CF30B1E66E11F9BD41E3D015FD427B923A7540578580B127A021D18CCC442A4C44EAF607F8181ECD075C91A672B3D1C0BEC1D2C937D74584BCDD0A5D1A402B1D094269C8661DBBA5929C6A634DBD63D4D6819A3B379AABA197B19CDDA29E2812ECAB8255F95DC922F1E6AA7D8B6EE3FB2007B8E232F30C318260F5E95263AB9F3C400BEC0903B4F1D15860BF6D9B6FA8C760D3D8793058F4395D2A1F3B80865196CC448F72322BA225EE8CC8CD101C9E01A79C4F250A5FC0EAE915FF4E9C068B2CBF0EEB291AC872463B0E4B22D6C78E78C8013B8A24ED883F8C523911235B3C8C77B116C24D1C212B3605BBA5C27294C8299584048D6F0E59E29B6ADF345ABE8FCFC7222334B95923B2A9F3E464EF1F561996D67358417DA763C8CB53796D8B66EB9B6AD5BAE6D5B41AC05EB6D7B6BF95F37D876A44C12DD75A57F6CB7E48B5F9466C361ADE2847DE4B8EC149E90FD5F267EFDBF6D3E292F542C7927E3D4921BA3A2FE6302D59F760B9BEEC0B6AD3BB018132ADD816D5B7760DB0EF6C0A4F01C3835B7CDB6679E149E608FF0E85D206B42A57781EC088FDE05F24566E1C7DD05C2E3BE20A4E9AED26D1C39D6DCB78666C1E3BD9F83D3C5AF5544C1ADDE8D4E6374785F9005974FBD1DDC7784B8E5FDE26592D7040C83D095CBA6F7D50366DE4A20871FF28817DE2F78775F2BAEE5AC06B2E966592F1AF775A38CBB55341105EF265E526487370B6F377247A690B82FBB893CA2E37DEF4CE4DD45CBCBCB5B93F04C2EC802FCBBAF304541742077EB5CB1B82F3BC5083FA08237D0A2CF4BD0DD2913ABC0833570A2030FB0C45AC39864F8127F1D679291664C4EFCFD77FFB9B52FEF6C6A9C1AA7C6FF8B639F415FC9F88F23B9D9D8488ED292388D640704F324CFAF61A4DF50EECE4E3626224DFFA7D835722D785766783C2249BB4EA2DF61E7EF8E614CF548BE74B2123A39DFF122FB979CCB09CDACE4FFAF38E9897F7F96F8E3C5997683283E48AC39E3C8E75CA3C8B8BDEE7D7D27BF596FFF3E79F9A1BE6E0F676A9C1AA7C6FFCF636FE7FF3EDDFE87CABDA7DE84AFBF25FAED174E4E52E70B47DD3E3B31CA36BE7B3AA4DFF02CB8B99CDFC9B758255F5F3AA389DF512737F1FF5C63E2ECF025B1DD7EB7BCF5294FECE91EFBD74B7E5C8FFFEAD6012F939379FB312BCA497DA92FF5A5BED497FA525FEA4B7DA92FF5A5BED497FA525FEAFB2FFDFE01AF7AEE8B]]>
              </MeshData>
            </MeshImport>
          </Producers>
        </Mesh>
        <Mesh Name="Mesh_CheckShapeSizes">
          <Producers>
            <MeshLoad Mesh="MESH_SHAPE_BOX"/>
            <MeshTransform Position="-3 0 0" Rotation="2 0 0"/>
            <MeshLoad Mesh="MESH_SHAPE_SPHERE"/>
            <MeshTransform Position="-1.5 0 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="MESH_SHAPE_CONE"/>
            <MeshCombine/>
            <MeshLoad Mesh="MESH_SHAPE_CYLINDER"/>
            <MeshTransform Position="1.5 0 0"/>
            <MeshCombine/>
            <MeshLoad Mesh="MESH_SHAPE_CAPSULE"/>
            <MeshTransform Position="3 0 0"/>
            <MeshCombine/>
          </Producers>
        </Mesh>
      </Children>
    </Group>
    <Group Comment="Collision Meshes">
      <Children>
        <Group Comment="Basic">
          <Children>
            <Mesh Name="MESH_SHAPE_CONE">
              <Producers>
                <MeshSphere Scale="0.5 0.5 0.5" ZSamples="3" RadialSamples="18"/>
                <MeshTransform Rotation="0.25 0 0"/>
                <MeshExpression Expression="v.Y = v.Y &lt; 0.25 ? -0.5 : 0.5;"/>
              </Producers>
            </Mesh>
            <Mesh Name="MESH_SHAPE_SPHERE">
              <Producers>
                <MeshBox Scale="1 0.5 1" XCount="18" YCount="12" Grid2DOnly="255"/>
                <MeshExpression Scale="0.5 0.5 0.5" AutoNormals="0">
                  <Expression>
<![CDATA[//

        float E, A, K, X, Y, Z;

        // Convert range to radians

        E = v.Y*PI; // Elevation
        A = v.X*PI; // Azimuth

        // Convert spherical coordinates into cartesian

        K = cos(E);

        X = sin(A)*K;
        Y = sin(E);
        Z = cos(A)*K;

        // Assign coordinates

        v.X = X;
        v.Y = Y;
        v.Z = Z;

        n.X = X;
        n.Y = Y;
        n.Z = Z;]]>
                  </Expression>
                </MeshExpression>
              </Producers>
            </Mesh>
            <Mesh Name="MESH_SHAPE_BOX">
              <Producers>
                <MeshBox Scale="0.5 0.5 0.5"/>
              </Producers>
            </Mesh>
            <Mesh Name="MESH_SHAPE_CYLINDER">
              <Producers>
                <MeshSphere ZSamples="6" RadialSamples="14"/>
                <MeshExpression AutoNormals="0">
                  <Expression>
<![CDATA[//V : current vertex
//VarP is set to the number of Z samples;
//VarQ is the number of circonferences wanted in the upper and lower faces
float VarP;
int VarQ = 1;
float Psi;

VarP = 6;//OrigSphere.ZSamples;
if (v.Y != 0 || v.X != 0) {
    Psi = atan2(v.Y,v.X);
    v.X = cos(Psi); //*pow(1-abs(v.Z),2);
    v.Y = sin(Psi);
    //Normals
    //n.X = cos(Psi);
    //n.Y = sin(Psi);

    if (VarQ == 0) {
        v.Z /= 1 - 2/(VarP-1);
        }
    else { //VarQ != 0
        v.Z /= 1 - (VarQ+1)*2/(VarP-1);
        if (abs(v.Z)>1.001) {   //this is because we are using floating point precision
                                     //it is better to avoid stuff like "Z > 1" due to truncament errors

            //remember: tallest's high: (1/(1 - (VarQ+1)*2/(VarP-1));
            v.X *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);
            v.Y *= 1 - (abs(v.Z)-1)/(1/(1 - (VarQ+1)*2/(VarP-1))-1);

            v.Z = v.Z/(abs(v.Z));

        }
    } //ELSE

    //Normals
    if (abs(v.Z) < 0.999) {
        n.Z = 0; //lateral surface
        }
    else {
        if (pow(v.X,2) + pow(v.Y,2) < 0.999) {
               n.X = 0;  //Upper surface
               n.Y = 0;
               }
        else {
                 //Edges normals, you can decide what to use.
        }
    }  // End of normals section

    //soft edges      Work In Progress
    /*
    if (abs(v.Z) > 0.7 && pow(v.X,2) + pow(v.Y,2) > 0.7) {
        v.Z *= 0.9;
        v.Y *= 0.9;
        v.X *= 0.9;
        }
    // */

} //OUTER IF]]>
                  </Expression>
                </MeshExpression>
                <MeshTransform Scale="0.5 0.5 0.5" Rotation="0.25 0 0"/>
              </Producers>
            </Mesh>
            <Mesh Name="MESH_SHAPE_CAPSULE">
              <Producers>
                <MeshBox Scale="0.5 0.5 1" XCount="15" Grid2DOnly="255"/>
                <MeshExpression AutoNormals="0">
                  <Expression>
<![CDATA[float RX, SX, CX;

RX = v.X*PI*2;

SX = sin(RX); CX = cos(RX);

v.X = SX/2;
v.Z = CX/2;

n.X = v.X;
n.Y = 0;
n.Z = v.Z;]]>
                  </Expression>
                </MeshExpression>
                <MeshBox Scale="0.5 0.5 1" XCount="15" YCount="3" Grid2DOnly="255"/>
                <MeshExpression AutoNormals="0">
                  <Expression>
<![CDATA[float RX, SX, CX,
      RY, SY, CY;

RX = v.X*PI*2;
RY = v.Y*PI/2-PI/4;

SX = sin(RX); CX = cos(RX);
SY = sin(RY); CY = cos(RY);

v.X = SX*CY/2;
v.Y = SY/2;
v.Z = CX*CY/2;

n.X = v.X;
n.Y = v.Y;
n.Z = v.Z;

v.Y -= 0.5;]]>
                  </Expression>
                </MeshExpression>
                <MeshCombine/>
                <MeshBox Scale="0.5 0.5 1" XCount="15" YCount="3" Grid2DOnly="255"/>
                <MeshExpression AutoNormals="0">
                  <Expression>
<![CDATA[float RX, SX, CX,
      RY, SY, CY;

RX = v.X*PI*2;
RY = v.Y*PI/2+PI/4;

SX = sin(RX); CX = cos(RX);
SY = sin(RY); CY = cos(RY);

v.X = SX*CY/2;
v.Y = SY/2;
v.Z = CX*CY/2;

n.X = v.X;
n.Y = v.Y;
n.Z = v.Z;

v.Y += 0.5;]]>
                  </Expression>
                </MeshExpression>
                <MeshCombine/>
              </Producers>
            </Mesh>
          </Children>
        </Group>
        <Group Comment="Compound">
          <Children>
            <Mesh Name="MESH_SHAPE_PYRAMID">
              <Producers>
                <MeshLoad Mesh="MESH_SHAPE_BOX"/>
                <MeshTransform Scale="2.12 1 2.12" Position="0 2.5 0" Rotation="0 -0.125 0"/>
                <MeshLoad Mesh="MESH_SHAPE_BOX"/>
                <MeshTransform Scale="3.18 1 3.18" Position="0 1.5 0" Rotation="0 -0.125 0"/>
                <MeshCombine/>
                <MeshLoad Mesh="MESH_SHAPE_BOX"/>
                <MeshTransform Scale="4.24 1 4.24" Position="0 0.5 0" Rotation="0 -0.125 0"/>
                <MeshCombine/>
                <MeshLoad Mesh="MESH_SHAPE_CONE"/>
                <MeshTransform Scale="1.5 1 1.5" Position="0 3.5 -0.01"/>
                <MeshCombine/>
              </Producers>
            </Mesh>
            <Mesh Name="MESH_SHAPE_PLANE">
              <Producers>
                <MeshLoad Mesh="MESH_SHAPE_BOX"/>
                <MeshTransform Scale="0.6 0.43 2.96" Position="-0.98 -0.17 1.01" Rotation="0 -0.086 0"/>
                <MeshLoad Mesh="MESH_SHAPE_BOX"/>
                <MeshTransform Scale="0.6 0.43 2.96" Position="0.98 -0.17 1.01" Rotation="0 0.086 0"/>
                <MeshCombine/>
                <MeshLoad Mesh="MESH_SHAPE_SPHERE"/>
                <MeshTransform Scale="1.33 1 4.41" Position="0 -0.1 -0.24" Rotation="-0.016 0 0"/>
                <MeshCombine/>
              </Producers>
            </Mesh>
          </Children>
        </Group>
      </Children>
    </Group>
  </Content>
</ZApplication>
And here's the Sketchup's ruby script:

Code: Select all

# Define your custom context menu item
module ZGEBullet

  # Export ZGEBullet shapes
  def self.export_to_zgebullet

    # Open the Ruby Console if not already open
    Sketchup.send_action('showRubyPanel:')

    SKETCHUP_CONSOLE.clear

    UI.start_timer(0.1) do # Make sure that the console is open before running the script

      # Check if any entities are selected
      if Sketchup.active_model.selection.empty?
        puts "No objects selected."
        return
      end

      create_arrays # Empty output arrays

      # Retrieve the first selected entity
      selected_entity = Sketchup.active_model.selection[0]


      # Check if the selected entity is a component or a group
      if selected_entity.is_a?(Sketchup::ComponentInstance) || selected_entity.is_a?(Sketchup::Group)

        instance_name = ""

        # If it's a group, iterate through each member
        if selected_entity.is_a?(Sketchup::Group)

          group = selected_entity

          # Retrieve the instance name of the group
          instance_name = group.name.upcase

          if instance_name == ""
            add_to_array("warning", "- SHAPE_NAME: You should give an instance name to this group.")
            instance_name = "NAME"
          end

          group.entities.each do |entity|
            process_entity(entity, true, instance_name)
          end


          # Get the transformation of the group
          transformation = group.transformation

          # Get the origin of the group's bounds
          origin = group.bounds.center

          # Transform the origin using the group's transformation
          inverse_transform = transformation.inverse
          local_origin = origin.transform(inverse_transform)

          conversion_factor = 0.0254 # So we need to convert inches to meters
          origin_x = sanitize_number((-local_origin.x * conversion_factor).round(2))
          origin_y = sanitize_number((-local_origin.z * conversion_factor).round(2))
          origin_z = sanitize_number((-local_origin.y * conversion_factor).round(2))

          add_to_array("center", "ModelViewerBodyCenter = vector3(#{origin_x}, #{origin_y}, #{origin_z});")


        else

          # Otherwise, process single entity
          process_entity(selected_entity)

        end

        print_result(instance_name)

      else
        puts "ERROR: The selected entity is not a component or group."
      end
    end

  end



  # Process a single entity and feed the global arrays with everything
  def self.process_entity(entity, inGroup = false, instance_name = "")

    definition = entity.definition
    shape = definition.name

    # Retrieve the transformation
    transformation = entity.transformation

    # IMPORTANT!
    #   SketchUp and ZGE don't have the same axis:
    #   ZGE.X = SketchUp.X
    #   ZGE.Y = SketchUp.Z
    #   ZGE.Z = -SketchUp.Y

    addChild = false

    # Check if the entity is within a group to calculate it's position and rotation
    if inGroup

      addChild = true

      # POSITION

      # Get the local position of the entity (in inches)
      conversion_factor = 0.0254 # So we need to convert inches to meters
      local_position_x = sanitize_number((transformation.origin.x * conversion_factor).round(2))
      local_position_y = sanitize_number((transformation.origin.z * conversion_factor).round(2))
      local_position_z = sanitize_number(-(transformation.origin.y * conversion_factor).round(2))


      # ROTATION

      # Get the transformation matrix
      a = transformation.to_a

      # Get angles in degree, then normalize from 0 to 1, and round 3 (useful because 0.125 = 22.5° = 45/2)
      rotation_x = sanitize_number(((rotX(transformation) * 180 / Math::PI) / 360).round(3))
      rotation_y = sanitize_number(((rotZ(transformation) * 180 / Math::PI) / 360).round(3))
      rotation_z = sanitize_number(((rotY(transformation) * 180 / Math::PI) / 360).round(3))

    end


    # SCALE

    # Calculate the initial vectors for each axis
    initial_vector_x = Geom::Vector3d.new(1, 0, 0).transform(transformation)
    initial_vector_y = Geom::Vector3d.new(0, 1, 0).transform(transformation)
    initial_vector_z = Geom::Vector3d.new(0, 0, 1).transform(transformation)

    # Calculate the scaling factors based on the length of the initial vectors
    scale_x = (transformation.xaxis.length * initial_vector_x.length).round(2)
    scale_y = (transformation.zaxis.length * initial_vector_z.length).round(2)
    scale_z = (transformation.yaxis.length * initial_vector_y.length).round(2)

    # Simplify the numbers for the distinct name of the object
    length_x = (scale_x * 1000).round(0)
    length_y = (scale_y * 1000).round(0)
    length_z = (scale_z * 1000).round(0)


    shape_name = ""

    # Output the results
    case shape

    when "SHAPE_SPHERE"

      if length_x == length_y && length_x == length_z

        # zbtCreateSphereShape(float radius)

        shape_name = "#{shape}_#{length_x}"
        add_to_array("name", "#{shape_name}")
        add_to_array("sphere", "#{shape_name} = zbtCreateSphereShape(#{scale_x/2});")

      else

        # zbtCreateScalableSphereShape(float radius)
        # zbtSetShapeLocalScaling(xptr shape, float x, float y, float z)

        shape_name = "#{shape}_#{length_x}x#{length_y}x#{length_z}"
        add_to_array("name", "#{shape_name}")
        add_to_array("scalable_sphere", "#{shape_name} = zbtCreateScalableSphereShape(1.0);\nzbtSetShapeLocalScaling(#{shape_name}, #{scale_x/2}, #{scale_y/2}, #{scale_z/2});")

      end


    when "SHAPE_BOX"

      # zbtCreateBoxShape(float x, float y, float z)

      shape_name = "#{shape}_#{length_x}x#{length_y}x#{length_z}"
      add_to_array("name", "#{shape_name}")
      add_to_array("box", "#{shape_name} = zbtCreateBoxShape(#{scale_x/2}, #{scale_y/2}, #{scale_z/2});")


    when "SHAPE_CONE"

      # zbtCreateConeShape(float radius, float height)

      if scale_x != scale_z
        add_to_array("warning", "- SHAPE_CONE: Scale x=#{scale_x} and z=#{scale_z} can't be different, so x value has been selected.")
        scale_z = scale_x
        length_z = length_x
      end

      shape_name = "#{shape}_#{length_x}x#{length_y}"
      add_to_array("name", "#{shape_name}")
      add_to_array("cone", "#{shape_name} = zbtCreateConeShape(#{scale_x/2}, #{scale_y});")


    when "SHAPE_CYLINDER"

      # zbtCreateCylinderShape(float radius, float height)

      if scale_x != scale_z
        add_to_array("warning", "- SHAPE_CYLINDER: Scale x=#{scale_x} and z=#{scale_z} can't be different, so x value has been selected.")
        scale_z = scale_x
        length_z = length_x
      end

      shape_name = "#{shape}_#{length_x}x#{length_y}"
      add_to_array("name", "#{shape_name}")
      add_to_array("cylinder", "#{shape_name} = zbtCreateCylinderShape(#{scale_x/2}, #{scale_y/2});")


    when "SHAPE_CAPSULE"

      # zbtCreateCapsuleShape(float radius, float height)

      if scale_x != scale_z
        add_to_array("warning", "- SHAPE_CAPSULE: Scale x=#{scale_x} and z=#{scale_z} can't be different, so x value has been selected.")
        scale_z = scale_x
        length_z = length_x
      end

      shape_name = "#{shape}_#{length_x}x#{length_y}"
      add_to_array("name", "#{shape_name}")
      add_to_array("capsule", "#{shape_name} = zbtCreateCapsuleShape(#{scale_x/2}, #{scale_y});")


    else

      add_to_array("warning", "- #{shape} isn't a ZGEBullet collision shape.")
      addChild = false

    end


    if addChild

      add_to_array("child","zbtAddChildShape(SHAPE_#{instance_name}, #{shape_name}, #{local_position_x},#{local_position_y},#{local_position_z}, #{rotation_x},#{rotation_y},#{rotation_z});")

      mesh_txt = "<MeshLoad Mesh=\"MESH_#{shape}\"/>\n<MeshTransform Scale=\"#{scale_x} #{scale_y} #{scale_z}\" Position=\"#{local_position_x} #{local_position_y} #{local_position_z}\" Rotation=\"#{rotation_x} #{rotation_y} #{rotation_z}\"/>"
      mesh_txt += "\n<MeshCombine/>" if @@array_mesh.length > 0
      add_to_array("mesh", mesh_txt)

    end


    add_to_array("scale", "return vector3(#{scale_x}, #{scale_y}, #{scale_z});") if scale_x != 1 || scale_y != 1 || scale_z != 1

  end



  # This is stupid but 0 can be negative in SketchUp so we convert -0.0 to positive 0.0
  def self.sanitize_number(number)
    number = 0 if number == -0
    return number
  end



  # Create all the global arrays
  def self.create_arrays()
    @@array_warning = []
    @@array_name = []
    @@array_box = []
    @@array_sphere = []
    @@array_scalable_sphere = []
    @@array_cone = []
    @@array_cylinder = []
    @@array_capsule = []
    @@array_child = []
    @@array_scale = []
    @@array_center = []
    @@array_mesh = []
  end
  


  # Add a value to a global array
  def self.add_to_array(array, value)
    @@array_warning << value if array == "warning"
    @@array_name << value if array == "name"
    @@array_box << value if array == "box"
    @@array_sphere << value if array == "sphere"
    @@array_scalable_sphere << value if array == "scalable_sphere"
    @@array_cone << value if array == "cone"
    @@array_cylinder << value if array == "cylinder"
    @@array_capsule << value if array == "capsule"
    @@array_child << value if array == "child"
    @@array_scale << value if array == "scale"
    @@array_center << value if array == "center"
    @@array_mesh << value if array == "mesh"
  end



  # Sort, remove duplicates and print an array item by item
  def self.print_array(array, newline = true, sanitize = true)
    array = array.uniq.sort if sanitize
    puts "" if newline && array.length > 0
    array.each do |item|
      puts item
    end
  end



  # Print all the results in the correct order
  # so we can simply copy/paste them in ZGameEditor
  def self.print_result(instance_name = "")

    # Useful warnings
    puts "WARNING:" if @@array_warning.length == 1
    puts "WARNINGS:" if @@array_warning.length > 1
    print_array(@@array_warning, false, false) # WARNING

    # List of shape names
    puts "\n\nCopy/Paste this list in the ZLibrary that defines the xptr shapes:"
    print_array(@@array_name) # NAME
    puts "SHAPE_#{instance_name}" if instance_name != ""

    # Definition of those shapes
    puts "\n\nCopy/Paste this list in the ZLibrary that creates those shapes:"
    print_array(@@array_box) # BOX
    print_array(@@array_sphere) # SPHERE    
    print_array(@@array_scalable_sphere) # SCALABLE SPHERE    
    print_array(@@array_cone) # CONE    
    print_array(@@array_cylinder) # CYLINDER    
    print_array(@@array_capsule) # CAPSULE

    if instance_name != ""

      # COMPOUND
      puts "\nSHAPE_#{instance_name} = zbtCreateCompoundShape();"
      print_array(@@array_child, false) # CHILD


      # Center DatabaseViewer Model on middle of the screen
      if @@array_center.length > 0
        puts "\n\nCopy/Paste this vector to the UpdateDatabaseCollisionMesh function:"
        print_array(@@array_center) # CENTER
      end


      # ZGE MESH in XML format
      puts "\n\nCopy/Paste this XML shape in the ZGameEditor project:"
      puts "\n<Mesh Name=\"MESH_SHAPE_#{instance_name}\">\n<Producers>"
      print_array(@@array_mesh, false) # MESH
      puts "</Producers>\n</Mesh>"

    else

      # Scale DatabaseViewer simple collision shapes
      if @@array_scale.length > 0
        puts "\n\nCopy/Paste this vector to the ScaleDatabaseCollisionMesh function:"
        print_array(@@array_scale) # SCALE
      end

    end

  end



  # Method to add the custom context menu item to right click
  def self.add_context_menu_item
    UI.add_context_menu_handler do |menu|
      menu.add_item("ZGEBullet Exporter") do
        export_to_zgebullet
      end
    end
  end



  # Clever math function to retrieve correct rotation of a SketchUp entity that has been scaled
 def self.euler_angle(tr, xyz=0)
  m = tr.xaxis.to_a + tr.yaxis.to_a + tr.zaxis.to_a
  if m[6] != 1 && m[6] != -1
    ry = -Math.asin(m[6])
    rx = Math.atan2(m[7]/Math.cos(ry), m[8]/Math.cos(ry))
    rz = Math.atan2(m[3]/Math.cos(ry), m[0]/Math.cos(ry))
  else
    rz = 0
    phi = Math.atan2(m[1], m[2])
    if m[6] == -1
      ry = Math::PI/2
      rx = rz + phi
    else
      ry = -Math::PI/2
      rx = -rz + phi
    end
  end   
  return -rx if xyz==0
  return -ry if xyz==1
  return -rz if xyz==2
 end
 def self.rotX(tr)
  # (Math.atan2(self.to_a[9],self.to_a[10]))
  # Math.acos(self.to_a[5])
  self.euler_angle(tr, 0)
 end
 def self.rotY(tr)
  # (Math.arcsin(self.to_a[8]))
  # Math.acos(self.to_a[0])
  self.euler_angle(tr, 1)
 end
 def self.rotZ(tr)
  # (-Math.atan2(self.to_a[4],self.to_a[0]))
  # Math.asin(self.to_a[4])
  self.euler_angle(tr, 2)
 end

end


# Call the method to add the custom context menu item
ZGEBullet.add_context_menu_item

As an example, here's what this script retrieve from the group constitued by the Pyramid and it's collision bounds:
Warnings:
- Groupe#2 isn't a ZGEBullet collision shape.
- SHAPE_CONE: Scale x=1.5 and z=1.52 can't be different, so x value has been selected.


Copy/Paste this list in the ZLibrary that defines the xptr shapes:

SHAPE_BOX_2120x1000x2120
SHAPE_BOX_3180x1000x3180
SHAPE_BOX_4240x1000x4240
SHAPE_CONE_1500x1000
SHAPE_PYRAMID


Copy/Paste this list in the ZLibrary that creates those shapes:

SHAPE_BOX_2120x1000x2120 = zbtCreateBoxShape(1.06, 0.5, 1.06);
SHAPE_BOX_3180x1000x3180 = zbtCreateBoxShape(1.59, 0.5, 1.59);
SHAPE_BOX_4240x1000x4240 = zbtCreateBoxShape(2.12, 0.5, 2.12);

SHAPE_CONE_1500x1000 = zbtCreateConeShape(0.75, 1.0);

SHAPE_PYRAMID = zbtCreateCompoundShape();
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_2120x1000x2120, 0,2.5,0, 0,-0.125,0);
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_3180x1000x3180, 0,1.5,0, 0,-0.125,0);
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_BOX_4240x1000x4240, 0,0.5,0, 0,-0.125,0);
zbtAddChildShape(SHAPE_PYRAMID, SHAPE_CONE_1500x1000, 0,3.5,-0.01, 0,0,0);


Copy/Paste this vector to the UpdateDatabaseCollisionMesh function:

ModelViewerBodyCenter = vector3(0, -2.0, 0);


Copy/Paste this XML shape in the ZGameEditor project:

<Mesh Name="MESH_SHAPE_PYRAMID">
<Producers>
<MeshLoad Mesh="MESH_SHAPE_BOX"/>
<MeshTransform Scale="2.12 1.0 2.12" Position="0 2.5 0" Rotation="0 -0.125 0"/>
<MeshLoad Mesh="MESH_SHAPE_BOX"/>
<MeshTransform Scale="3.18 1.0 3.18" Position="0 1.5 0" Rotation="0 -0.125 0"/>
<MeshCombine/>
<MeshLoad Mesh="MESH_SHAPE_BOX"/>
<MeshTransform Scale="4.24 1.0 4.24" Position="0 0.5 0" Rotation="0 -0.125 0"/>
<MeshCombine/>
<MeshLoad Mesh="MESH_SHAPE_CONE"/>
<MeshTransform Scale="1.5 1.0 1.5" Position="0 3.5 -0.01" Rotation="0 0 0"/>
<MeshCombine/>
</Producers>
</Mesh>

I'll see how it goes when adding content to Omeganaut, and modify those scripts as needed. And I'll push it to Github at some point, in case anyone else wants to use ZGameEditor, ZGEBullet, and SketchUp to do something complicated :lol:
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Kjell »

Hi Ats,
Ats wrote: Tue Jul 04, 2023 7:32 pmI can simply retrieve the offset in my sketchup ruby script and store it with the rest, it will be easier and ready for release too.
That's definetely more convenient than having to do it in ZGE.
Ats wrote: Tue Jul 04, 2023 7:32 pmThe sketchup and ZGE axis don't have the same rotation
Right, there's no consensus .. software like Blender and SketchUp use the z-axis as up, while software like Maya and Softimage use the y-axis as up.
Ats wrote: Tue Jul 04, 2023 7:32 pmScaling an entity in SketchUp will complicate the way you can retrieve its rotation, so I had to use this: https://forums.sketchup.com/t/how-to-fi ... ty/65295/6
Weird & unconvenient that it only gives you the transformation matrix ( instead of individual translation / rotation / scale values ) :cry:
Ats wrote: Wed Jul 05, 2023 1:49 pmAnd it's done!
Nice .. good job! No support for zbtCreateConvexHullShape and zbtCreateTriangleMeshShape ( yet ) though?
Ats wrote: Wed Jul 05, 2023 1:49 pmThe whole thing can be optimized, but it's working and already saving me tons of time.
When it comes to converters that you only use during development i wouldn't spend too much time on optimizations as it doesn't affect your actual game.

K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

No support for zbtCreateConvexHullShape and zbtCreateTriangleMeshShape ( yet ) though?
All right, I think that I'm going to try doing those, because I can't easily use basic collision shapes for models like that:
Screenshot 2023-10-05 104615.png
Screenshot 2023-10-05 104615.png (72.01 KiB) Viewed 97544 times
Here's ZGEBullet source for triangleMeshShape:

Code: Select all

EXPORT btCollisionShape* zbtCreateTriangleMeshShape(float* triangles, int numTriangles, int meshType) {

	// create triangle mesh
	btTriangleMesh* triangleMesh = new btTriangleMesh(false, false);

	// add triangles
	try {
		for (int i = (numTriangles-1) * 9; i >= 0; --i)
			triangleMesh->addTriangle(
				btVector3(triangles[i], triangles[i+1], triangles[i+2]),
				btVector3(triangles[i+3], triangles[i+4], triangles[i+5]),
				btVector3(triangles[i+6], triangles[i+7], triangles[i+8]), true);
	} catch (...) { return NULL; }

	// create triangle mesh collision shape

	btCollisionShape* tm;

	switch (meshType) {
	case 1: // convex hull
		tm = new btConvexTriangleMeshShape(triangleMesh);
		break;
	case 2: // concave static-triangle mesh shape
		tm = new btBvhTriangleMeshShape(triangleMesh, true, true);
		break;
	case 3: // concave deformable mesh
		tm = new btGImpactMeshShape(triangleMesh);
		static_cast<btGImpactMeshShape*>(tm)->updateBound();
	}

	ADD_COLLISION_SHAPE(tm);
}
Just to be sure, what float* triangles array would look in ZGE for a single triangle, something like this?

{
Triangles.SizeDim1=9;
Triangles[0]=Ax;
Triangles[1]=Ay;
Triangles[2]=Az;
Triangles[3]=Bx;
Triangles[4]=By;
Triangles[5]=Bz;
Triangles[6]=Cx;
Triangles[7]=Cy;
Triangles[8]=Cz;
}
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Kjell »

Hi Ats,
Ats wrote: Thu Oct 05, 2023 8:53 amJust to be sure, what float* triangles array would look in ZGE for a single triangle, something like this?
Yup .. the btTriangleMesh(false, false) call gives you a mesh with 3-components ( XYZ ) per vertex using 16-bit indices ( so no more than 65536 vertices total ). Then the addTriangle call has removeDuplicateVertices set to true, so any duplicate vertices automatically get removed from the vertex data and the corresponding pre-existing indices get used instead. Be aware that this feature gets increasingly slow* the more unique vertices your mesh has. I suspect Rado opted for this approach out of convenience as it prevents you from having to prepare a separate index array yourself.

*Only once when calling zbtCreateTriangleMeshShape, it doesn't affect performance afterwards ( but it does save memory of course ).

K
User avatar
Ats
Posts: 770
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Ats »

Maybe zbtCreateTriangleMeshShape is overkill for a floating rock.
As the most common shape is a deformed (or not) pyramid, that the basic ConeShape can't cover:
Screenshot 2023-10-06 092154.png
Screenshot 2023-10-06 092154.png (15.54 KiB) Viewed 97516 times
Here's zbtCreateConvexHullShape sourcecode:

Code: Select all

/*
@fn xptr zbtCreateConvexHullShape(
	xptr points, int numPoints)
@brief Create convex hull shape given by array of vertices.
@details zbtCreateConvexHullShape() makes an internal copy of the points.
@param points pointer to array of floats; each point is given by 3 consequent
	float values representing (x, y, z) vector
@param numPoints number of points
@return pointer to created collision shape
*/

EXPORT btCollisionShape* zbtCreateConvexHullShape(float* points, int numPoints) {

	try {
		btConvexHullShape* ch = new btConvexHullShape(points, numPoints, sizeof(float) * 3);

		//create a hull approximation - optimization of hull points
		btShapeHull* hull = new btShapeHull(ch);
		hull->buildHull(ch->getMargin());
		delete ch;

		ADD_COLLISION_SHAPE(new btConvexHullShape((const float*)hull->getVertexPointer(), hull->numVertices()));
	} catch (...) { return NULL; }
}
Contrary to the zbtCreateTriangleMeshShape function, I don't get the points array. Should they be stored in a certain order?

Apparently, the function is creating a hull approximation, so maybe I could simply pass the points of my floating rock to it. But the final package, being convex, would not be good. So, instead, I could use a CompoundShape made of 5 ConvexHullShape pyramids. It should be pretty straight forward. But would it be faster than a single TriangleMeshShape?
User avatar
Kjell
Posts: 1915
Joined: Sat Feb 23, 2008 11:15 pm

Re: ZGEBullet Collision Shape Viewer / Editor

Post by Kjell »

Hi Ats,
Ats wrote: Fri Oct 06, 2023 7:33 amContrary to the zbtCreateTriangleMeshShape function, I don't get the points array. Should they be stored in a certain order?
Nope .. the points don't have to be in a certain order. As quoted from the Panda3D documentation: A good analogy for a convex hull is an elastic membrane or balloon under pressure which is placed around a given set of vertices. When released the membrane will assume the shape of the convex hull.

So in 2D that would look like this ( it basically works the same in 3D ):

Image

+ Of course pre-filtering your points so you only add the ones that make up the convex hull is recommended.
Ats wrote: Fri Oct 06, 2023 7:33 amSo, instead, I could use a CompoundShape made of 5 ConvexHullShape pyramids. It should be pretty straight forward. But would it be faster than a single TriangleMeshShape?
Not sure which one would be faster. I could take a guess, but i'd suggest benchmarking it instead.

K
Post Reply