1 /* 2 Copyright (c) 2017-2018 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.logics.charactercontroller; 29 30 import std.math; 31 32 import dlib.core.memory; 33 import dlib.math.vector; 34 import dlib.math.matrix; 35 import dlib.math.transformation; 36 import dlib.math.utils; 37 38 import dagon.logics.entity; 39 import dagon.logics.controller; 40 import dagon.physics.world; 41 import dagon.physics.rigidbody; 42 import dagon.physics.geometry; 43 import dagon.physics.shape; 44 import dagon.physics.raycast; 45 46 /* 47 * CharacterController implements kinematic body on top of dmech dynamics: it allows direct 48 * velocity changes for a RigidBody. CharacterController is intended for generic action game 49 * character movement. 50 */ 51 class CharacterController: EntityController 52 { 53 PhysicsWorld world; 54 RigidBody rbody; 55 bool onGround = false; 56 Vector3f direction = Vector3f(0, 0, 1); 57 float speed = 0.0f; 58 float jSpeed = 0.0f; 59 float maxVelocityChange = 0.75f; 60 float artificalGravity = 50.0f; 61 Vector3f rotation; 62 RigidBody floorBody; 63 Vector3f floorNormal; 64 bool flyMode = false; 65 bool clampY = true; 66 ShapeComponent sensor; 67 float selfTurn = 0.0f; 68 69 this(Entity e, PhysicsWorld world, float mass, Geometry geom) 70 { 71 super(e); 72 this.world = world; 73 rbody = world.addDynamicBody(e.position); 74 rbody.bounce = 0.0f; 75 rbody.friction = 1.0f; 76 rbody.enableRotation = false; 77 rbody.useOwnGravity = true; 78 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 79 rbody.raycastable = false; 80 world.addShapeComponent(rbody, geom, Vector3f(0, 0, 0), mass); 81 rotation = Vector3f(0, 0, 0); // TODO: get from e 82 } 83 84 ShapeComponent createSensor(Geometry geom, Vector3f point) 85 { 86 if (sensor is null) 87 sensor = world.addSensor(rbody, geom, point); 88 return sensor; 89 } 90 91 void enableGravity(bool mode) 92 { 93 flyMode = !mode; 94 95 if (mode) 96 { 97 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 98 } 99 else 100 { 101 rbody.gravity = Vector3f(0, 0, 0); 102 } 103 } 104 105 void logicalUpdate() 106 { 107 Vector3f targetVelocity = direction * speed; 108 109 if (!flyMode) 110 { 111 onGround = checkOnGround(); 112 113 if (onGround) 114 rbody.gravity = Vector3f(0.0f, -artificalGravity * 0.1f, 0.0f); 115 else 116 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 117 118 selfTurn = 0.0f; 119 if (onGround && floorBody) 120 { 121 Vector3f relPos = rbody.position - floorBody.position; 122 Vector3f rotVel = cross(floorBody.angularVelocity, relPos); 123 targetVelocity += floorBody.linearVelocity; 124 if (!floorBody.dynamic) 125 { 126 targetVelocity += rotVel; 127 selfTurn = -floorBody.angularVelocity.y; 128 } 129 } 130 131 speed = 0.0f; 132 jSpeed = 0.0f; 133 } 134 else 135 { 136 speed *= 0.95f; 137 jSpeed *= 0.95f; 138 } 139 140 Vector3f velocityChange = targetVelocity - rbody.linearVelocity; 141 velocityChange.x = clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange); 142 velocityChange.z = clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange); 143 144 if (clampY && !flyMode) 145 velocityChange.y = 0; 146 else 147 velocityChange.y = clamp(velocityChange.y, -maxVelocityChange, maxVelocityChange); 148 149 rbody.linearVelocity += velocityChange; 150 } 151 152 override void update(double dt) 153 { 154 entity.position = rbody.position; 155 entity.rotation = rbody.orientation; 156 entity.transformation = rbody.transformation * scaleMatrix(entity.scaling); 157 entity.invTransformation = entity.transformation.inverse; 158 } 159 160 bool checkOnGround() 161 { 162 floorBody = null; 163 CastResult cr; 164 bool hit = world.raycast(rbody.position, Vector3f(0, -1, 0), 10, cr, true, true); 165 if (hit) 166 { 167 floorBody = cr.rbody; 168 floorNormal = cr.normal; 169 } 170 171 if (sensor) 172 { 173 if (sensor.numCollisions > 0) 174 return true; 175 } 176 177 return false; 178 } 179 180 void turn(float angle) 181 { 182 rotation.y += angle; 183 } 184 185 void move(Vector3f direction, float spd) 186 { 187 this.direction = direction; 188 this.speed = spd; 189 } 190 191 void jump(float height) 192 { 193 if (onGround || flyMode) 194 { 195 jSpeed = jumpSpeed(height); 196 rbody.linearVelocity.y = jSpeed; 197 } 198 } 199 200 float jumpSpeed(float jumpHeight) 201 { 202 return sqrt(2.0f * jumpHeight * artificalGravity); 203 } 204 }