|
| 1 | +/** |
| 2 | + * Quaternion minimal para rotações 3D e SLERP. |
| 3 | + */ |
| 4 | +class Quaternion { |
| 5 | + float w, x, y, z; |
| 6 | + |
| 7 | + Quaternion(float w_, float x_, float y_, float z_) { |
| 8 | + w = w_; x = x_; y = y_; z = z_; |
| 9 | + } |
| 10 | + |
| 11 | + // constrói a partir de eixo (unitário) e ângulo (rad) |
| 12 | + Quaternion fromAxisAngle(PVector axis, float angle) { |
| 13 | + float half = angle * 0.5f; |
| 14 | + float s = sin(half); |
| 15 | + return new Quaternion( |
| 16 | + cos(half), |
| 17 | + axis.x * s, |
| 18 | + axis.y * s, |
| 19 | + axis.z * s |
| 20 | + ).normalize(); |
| 21 | + } |
| 22 | + |
| 23 | + // normaliza |
| 24 | + Quaternion normalize() { |
| 25 | + float m = sqrt(w*w + x*x + y*y + z*z); |
| 26 | + w /= m; x /= m; y /= m; z /= m; |
| 27 | + return this; |
| 28 | + } |
| 29 | + |
| 30 | + // compõe rotações |
| 31 | + Quaternion multiply(Quaternion q) { |
| 32 | + return new Quaternion( |
| 33 | + w*q.w - x*q.x - y*q.y - z*q.z, |
| 34 | + w*q.x + x*q.w + y*q.z - z*q.y, |
| 35 | + w*q.y - x*q.z + y*q.w + z*q.x, |
| 36 | + w*q.z + x*q.y - y*q.x + z*q.w |
| 37 | + ); |
| 38 | + } |
| 39 | + |
| 40 | + // converte para matriz col-major 4×4 |
| 41 | + float[] toMatrix() { |
| 42 | + float[] m = new float[16]; |
| 43 | + m[0] = 1 - 2*(y*y + z*z); |
| 44 | + m[1] = 2*(x*y + z*w); |
| 45 | + m[2] = 2*(x*z - y*w); |
| 46 | + m[3] = 0; |
| 47 | + |
| 48 | + m[4] = 2*(x*y - z*w); |
| 49 | + m[5] = 1 - 2*(x*x + z*z); |
| 50 | + m[6] = 2*(y*z + x*w); |
| 51 | + m[7] = 0; |
| 52 | + |
| 53 | + m[8] = 2*(x*z + y*w); |
| 54 | + m[9] = 2*(y*z - x*w); |
| 55 | + m[10] = 1 - 2*(x*x + y*y); |
| 56 | + m[11] = 0; |
| 57 | + |
| 58 | + m[12] = m[13] = m[14] = 0; |
| 59 | + m[15] = 1; |
| 60 | + return m; |
| 61 | + } |
| 62 | + |
| 63 | + // SLERP entre this e q2 |
| 64 | + Quaternion slerp(Quaternion q2, float t) { |
| 65 | + float dot = w*q2.w + x*q2.x + y*q2.y + z*q2.z; |
| 66 | + dot = constrain(dot, -1, 1); |
| 67 | + float theta = acos(dot); |
| 68 | + if (theta < 1e-6) return this; |
| 69 | + float sinT = sin(theta); |
| 70 | + float w1 = sin((1 - t) * theta) / sinT; |
| 71 | + float w2 = sin(t * theta) / sinT; |
| 72 | + return new Quaternion( |
| 73 | + w1*w + w2*q2.w, |
| 74 | + w1*x + w2*q2.x, |
| 75 | + w1*y + w2*q2.y, |
| 76 | + w1*z + w2*q2.z |
| 77 | + ).normalize(); |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | + |
| 82 | +/** |
| 83 | + * Controlador de câmera com quaternion para rotações estáveis |
| 84 | + * e interpolação suave entre posições e orientações. |
| 85 | + */ |
| 86 | +class CameraController { |
| 87 | + PVector target; // ponto que a câmera olha |
| 88 | + float distance; // distância ao target |
| 89 | + Quaternion orientation; // orientação atual |
| 90 | + |
| 91 | + PVector goalTarget; |
| 92 | + Quaternion goalOrientation; |
| 93 | + float goalDistance; |
| 94 | + float lerpFactor = 0.1f; // suavização |
| 95 | + |
| 96 | + CameraController(PVector initialTarget, float initialDistance) { |
| 97 | + target = initialTarget.copy(); |
| 98 | + goalTarget = initialTarget.copy(); |
| 99 | + distance = initialDistance; |
| 100 | + goalDistance = initialDistance; |
| 101 | + orientation = new Quaternion(1, 0, 0, 0); |
| 102 | + goalOrientation = orientation; |
| 103 | + } |
| 104 | + |
| 105 | + // aplica no PGraphicsOpenGL |
| 106 | + void apply(PGraphicsOpenGL pg) { |
| 107 | + pg.translate(0, 0, -distance); |
| 108 | + float[] M = orientation.toMatrix(); |
| 109 | + pg.applyMatrix( |
| 110 | + M[0], M[4], M[8], M[12], |
| 111 | + M[1], M[5], M[9], M[13], |
| 112 | + M[2], M[6], M[10], M[14], |
| 113 | + M[3], M[7], M[11], M[15] |
| 114 | + ); |
| 115 | + pg.translate(-target.x, -target.y, -target.z); |
| 116 | + } |
| 117 | + |
| 118 | + // atualiza cada frame (SLERP/LERP) |
| 119 | + void update() { |
| 120 | + orientation = orientation.slerp(goalOrientation, lerpFactor); |
| 121 | + target = PVector.lerp(target, goalTarget, lerpFactor); |
| 122 | + distance = lerp(distance, goalDistance, lerpFactor); |
| 123 | + } |
| 124 | + |
| 125 | + // define nova meta (alvo, orientação, distância) |
| 126 | + void goTo(PVector t, Quaternion o, float d) { |
| 127 | + goalTarget = t.copy(); |
| 128 | + goalOrientation = o.normalize(); |
| 129 | + goalDistance = d; |
| 130 | + } |
| 131 | + |
| 132 | + // rotaciona em torno de um eixo do mundo |
| 133 | + void rotateAround(PVector axis, float angle) { |
| 134 | + Quaternion delta = new Quaternion(1,0,0,0).fromAxisAngle(axis, angle); |
| 135 | + orientation = delta.multiply(orientation).normalize(); |
| 136 | + goalOrientation = orientation; |
| 137 | + } |
| 138 | + |
| 139 | + // setters diretos (sem suavização) |
| 140 | + void setTarget(PVector t) { target = goalTarget = t.copy(); } |
| 141 | + void setOrientation(Quaternion q) { |
| 142 | + orientation = goalOrientation = q.normalize(); |
| 143 | + } |
| 144 | + void setDistance(float d) { distance = goalDistance = d; } |
| 145 | + |
| 146 | + // getters |
| 147 | + PVector getTarget() { return target.copy(); } |
| 148 | + float getDistance() { return distance; } |
| 149 | + Quaternion getOrientation() { return orientation; } |
| 150 | + |
| 151 | + // utilitário lerp |
| 152 | + float lerp(float a, float b, float f) { |
| 153 | + return a + (b - a) * f; |
| 154 | + } |
| 155 | +} |
0 commit comments