1 module cc;
2 
3 import std.math;
4 
5 import dlib.math.vector;
6 import dlib.math.matrix;
7 import dlib.math.affine;
8 import dlib.math.utils;
9 
10 import dmech.world;
11 import dmech.rigidbody;
12 import dmech.geometry;
13 import dmech.raycast;
14 import dmech.contact;
15 
16 /*
17  * CharacterController implements kinematic body on top of dmech dynamics: 
18  * it allows direct velocity changes for a RigidBody.
19  * CharacterController is intended for generic action game character movement.
20  */
21 class CharacterController
22 {
23     PhysicsWorld world;
24     RigidBody rbody;
25     bool onGround = false;
26     Vector3f direction = Vector3f(0, 0, 1);
27     float speed = 0.0f;
28     float maxVelocityChange = 0.75f;
29     float artificalGravity = 50.0f;
30     Matrix4x4f localMatrix;
31     Vector3f rotation;
32 
33     this(PhysicsWorld world, Vector3f pos, float mass, Geometry geom = null)
34     {
35         this.world = world;
36         rbody = world.addDynamicBody(pos);
37         rbody.bounce = 0.0f;
38         rbody.enableRotation = false;
39         rbody.useOwnGravity = true;
40         rbody.gravity = Vector3f(0.0f, -artificalGravity, 0.0f);
41         rbody.raycastable = false;
42         if (geom is null)
43             geom = new GeomSphere(1.0f);
44         world.addShapeComponent(rbody, geom, Vector3f(0, 0, 0), mass);
45         localMatrix = Matrix4x4f.identity;
46         rotation = Vector3f(0, 0, 0);
47 
48         rbody.onNewContact ~= (RigidBody b, Contact c)
49         {
50             if (dot((c.point - b.position).normalized, rbody.gravity.normalized) > 0.5f)
51             {
52                 onGround = true;
53             }
54         };
55     }
56     
57     void updateMatrix()
58     {
59         localMatrix = Matrix4x4f.identity;
60         localMatrix *= rotationMatrix(Axis.x, degtorad(rotation.x));
61         localMatrix *= rotationMatrix(Axis.y, degtorad(rotation.y));
62         localMatrix *= rotationMatrix(Axis.z, degtorad(rotation.z));
63 
64         direction = localMatrix.forward;
65     }
66 
67     void update(bool clampY = true)
68     {
69         Vector3f targetVelocity = direction * speed;
70 
71         Vector3f velocityChange = targetVelocity - rbody.linearVelocity;
72         velocityChange.x = clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
73         velocityChange.z = clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
74         if (clampY)
75             velocityChange.y = 0;
76         else
77             velocityChange.y = clamp(velocityChange.y, -maxVelocityChange, maxVelocityChange);
78         rbody.linearVelocity += velocityChange;
79 
80         speed = 0.0f;
81     }
82 
83     bool checkOnGround()
84     {
85         CastResult cr;
86         bool hit = world.raycast(rbody.position, Vector3f(0, -1, 0), 10, cr, true, true);
87         if (hit)
88         {
89             if (distance(cr.point, rbody.position) <= 1.1f)
90                 return true;
91         }
92         return false;
93     }
94 
95     void turn(float angle)
96     {
97         rotation.y += angle;
98     }
99 
100     void move(float spd)
101     {
102         speed = spd;
103     }
104 
105     void jump(float height)
106     {
107         if (onGround || checkOnGround)
108         {
109             rbody.linearVelocity.y = jumpSpeed(height);
110             onGround = false;
111         }
112     }
113 
114     float jumpSpeed(float jumpHeight)
115     {
116         return sqrt(2.0f * jumpHeight * artificalGravity);
117     }
118 }