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 dmech; 41 42 /* 43 * CharacterController implements kinematic body on top of dmech dynamics: it allows direct 44 * velocity changes for a RigidBody. CharacterController is intended for generic action game 45 * character movement. 46 */ 47 class CharacterController: EntityController 48 { 49 PhysicsWorld world; 50 RigidBody rbody; 51 bool onGround = false; 52 Vector3f direction = Vector3f(0, 0, 1); 53 float speed = 0.0f; 54 float jSpeed = 0.0f; 55 float maxVelocityChange = 0.75f; 56 float artificalGravity = 50.0f; 57 Vector3f rotation; 58 RigidBody floorBody; 59 Vector3f floorNormal; 60 bool flyMode = false; 61 bool clampY = true; 62 ShapeComponent sensor; 63 float selfTurn = 0.0f; 64 65 this(Entity e, PhysicsWorld world, float mass, Geometry geom) 66 { 67 super(e); 68 this.world = world; 69 rbody = world.addDynamicBody(e.position); 70 rbody.bounce = 0.0f; 71 rbody.friction = 1.0f; 72 rbody.enableRotation = false; 73 rbody.useOwnGravity = true; 74 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 75 rbody.raycastable = false; 76 world.addShapeComponent(rbody, geom, Vector3f(0, 0, 0), mass); 77 rotation = Vector3f(0, 0, 0); // TODO: get from e 78 } 79 80 ShapeComponent createSensor(Geometry geom, Vector3f point) 81 { 82 if (sensor is null) 83 sensor = world.addSensor(rbody, geom, point); 84 return sensor; 85 } 86 87 void enableGravity(bool mode) 88 { 89 flyMode = !mode; 90 91 if (mode) 92 { 93 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 94 } 95 else 96 { 97 rbody.gravity = Vector3f(0, 0, 0); 98 } 99 } 100 101 void logicalUpdate() 102 { 103 Vector3f targetVelocity = direction * speed; 104 105 if (!flyMode) 106 { 107 onGround = checkOnGround(); 108 109 if (onGround) 110 rbody.gravity = Vector3f(0.0f, -artificalGravity * 0.1f, 0.0f); 111 else 112 rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f); 113 114 selfTurn = 0.0f; 115 if (onGround && floorBody) 116 { 117 Vector3f relPos = rbody.position - floorBody.position; 118 Vector3f rotVel = cross(floorBody.angularVelocity, relPos); 119 targetVelocity += floorBody.linearVelocity; 120 if (!floorBody.dynamic) 121 { 122 targetVelocity += rotVel; 123 selfTurn = -floorBody.angularVelocity.y; 124 } 125 } 126 127 speed = 0.0f; 128 jSpeed = 0.0f; 129 } 130 else 131 { 132 speed *= 0.95f; 133 jSpeed *= 0.95f; 134 } 135 136 Vector3f velocityChange = targetVelocity - rbody.linearVelocity; 137 velocityChange.x = clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange); 138 velocityChange.z = clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange); 139 140 if (clampY && !flyMode) 141 velocityChange.y = 0; 142 else 143 velocityChange.y = clamp(velocityChange.y, -maxVelocityChange, maxVelocityChange); 144 145 rbody.linearVelocity += velocityChange; 146 } 147 148 override void update(double dt) 149 { 150 entity.position = rbody.position; 151 entity.rotation = rbody.orientation; 152 entity.transformation = rbody.transformation * scaleMatrix(entity.scaling); 153 entity.invTransformation = entity.transformation.inverse; 154 } 155 156 bool checkOnGround() 157 { 158 floorBody = null; 159 CastResult cr; 160 bool hit = world.raycast(rbody.position, Vector3f(0, -1, 0), 10, cr, true, true); 161 if (hit) 162 { 163 floorBody = cr.rbody; 164 floorNormal = cr.normal; 165 } 166 167 if (sensor) 168 { 169 if (sensor.numCollisions > 0) 170 return true; 171 } 172 173 return false; 174 } 175 176 void turn(float angle) 177 { 178 rotation.y += angle; 179 } 180 181 void move(Vector3f direction, float spd) 182 { 183 this.direction = direction; 184 this.speed = spd; 185 } 186 187 void jump(float height) 188 { 189 if (onGround || flyMode) 190 { 191 jSpeed = jumpSpeed(height); 192 rbody.linearVelocity.y = jSpeed; 193 } 194 } 195 196 float jumpSpeed(float jumpHeight) 197 { 198 return sqrt(2.0f * jumpHeight * artificalGravity); 199 } 200 }