Compare commits
2 Commits
31ac22bd0f
...
b196683e4a
| Author | SHA1 | Date | |
|---|---|---|---|
| b196683e4a | |||
| 535c397be2 |
@@ -227,6 +227,23 @@ void Ball::switchGravity() {
|
|||||||
gravity_force_ = gravity_force_ == 0.0f ? (GRAVITY_FORCE * 60.0f * 60.0f) : 0.0f;
|
gravity_force_ = gravity_force_ == 0.0f ? (GRAVITY_FORCE * 60.0f * 60.0f) : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reactiva la gravedad si está desactivada
|
||||||
|
void Ball::enableGravityIfDisabled() {
|
||||||
|
if (gravity_force_ == 0.0f) {
|
||||||
|
gravity_force_ = GRAVITY_FORCE * 60.0f * 60.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuerza gravedad ON (siempre activa)
|
||||||
|
void Ball::forceGravityOn() {
|
||||||
|
gravity_force_ = GRAVITY_FORCE * 60.0f * 60.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuerza gravedad OFF (siempre desactiva)
|
||||||
|
void Ball::forceGravityOff() {
|
||||||
|
gravity_force_ = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Cambia la dirección de gravedad
|
// Cambia la dirección de gravedad
|
||||||
void Ball::setGravityDirection(GravityDirection direction) {
|
void Ball::setGravityDirection(GravityDirection direction) {
|
||||||
gravity_direction_ = direction;
|
gravity_direction_ = direction;
|
||||||
@@ -283,12 +300,30 @@ void Ball::setDepthBrightness(float brightness) {
|
|||||||
// Activar/desactivar atracción física hacia esfera RotoBall
|
// Activar/desactivar atracción física hacia esfera RotoBall
|
||||||
void Ball::enableRotoBallAttraction(bool enable) {
|
void Ball::enableRotoBallAttraction(bool enable) {
|
||||||
rotoball_attraction_active_ = enable;
|
rotoball_attraction_active_ = enable;
|
||||||
|
|
||||||
|
// Al activar atracción, resetear flags de superficie para permitir física completa
|
||||||
|
if (enable) {
|
||||||
|
on_surface_ = false;
|
||||||
|
stopped_ = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aplicar fuerza de resorte hacia punto objetivo en esfera rotante
|
// Aplicar fuerza de resorte hacia punto objetivo en esfera rotante
|
||||||
void Ball::applyRotoBallForce(float target_x, float target_y, float deltaTime) {
|
void Ball::applyRotoBallForce(float target_x, float target_y, float sphere_radius, float deltaTime) {
|
||||||
if (!rotoball_attraction_active_) return;
|
if (!rotoball_attraction_active_) return;
|
||||||
|
|
||||||
|
// Calcular factor de escala basado en el radio (radio base = 80px)
|
||||||
|
// Si radius=80 → scale=1.0, si radius=160 → scale=2.0, si radius=360 → scale=4.5
|
||||||
|
const float BASE_RADIUS = 80.0f;
|
||||||
|
float scale = sphere_radius / BASE_RADIUS;
|
||||||
|
|
||||||
|
// Escalar constantes de física proporcionalmente
|
||||||
|
float spring_k = ROTOBALL_SPRING_K * scale;
|
||||||
|
float damping_base = ROTOBALL_DAMPING_BASE * scale;
|
||||||
|
float damping_near = ROTOBALL_DAMPING_NEAR * scale;
|
||||||
|
float near_threshold = ROTOBALL_NEAR_THRESHOLD * scale;
|
||||||
|
float max_force = ROTOBALL_MAX_FORCE * scale;
|
||||||
|
|
||||||
// Calcular vector diferencia (dirección hacia el target)
|
// Calcular vector diferencia (dirección hacia el target)
|
||||||
float diff_x = target_x - pos_.x;
|
float diff_x = target_x - pos_.x;
|
||||||
float diff_y = target_y - pos_.y;
|
float diff_y = target_y - pos_.y;
|
||||||
@@ -297,13 +332,13 @@ void Ball::applyRotoBallForce(float target_x, float target_y, float deltaTime) {
|
|||||||
float distance = sqrtf(diff_x * diff_x + diff_y * diff_y);
|
float distance = sqrtf(diff_x * diff_x + diff_y * diff_y);
|
||||||
|
|
||||||
// Fuerza de resorte (Ley de Hooke: F = -k * x)
|
// Fuerza de resorte (Ley de Hooke: F = -k * x)
|
||||||
float spring_force_x = ROTOBALL_SPRING_K * diff_x;
|
float spring_force_x = spring_k * diff_x;
|
||||||
float spring_force_y = ROTOBALL_SPRING_K * diff_y;
|
float spring_force_y = spring_k * diff_y;
|
||||||
|
|
||||||
// Amortiguación variable: más cerca del punto = más amortiguación (estabilización)
|
// Amortiguación variable: más cerca del punto = más amortiguación (estabilización)
|
||||||
float damping = (distance < ROTOBALL_NEAR_THRESHOLD)
|
float damping = (distance < near_threshold)
|
||||||
? ROTOBALL_DAMPING_NEAR
|
? damping_near
|
||||||
: ROTOBALL_DAMPING_BASE;
|
: damping_base;
|
||||||
|
|
||||||
// Fuerza de amortiguación (proporcional a la velocidad)
|
// Fuerza de amortiguación (proporcional a la velocidad)
|
||||||
float damping_force_x = damping * vx_;
|
float damping_force_x = damping * vx_;
|
||||||
@@ -315,10 +350,10 @@ void Ball::applyRotoBallForce(float target_x, float target_y, float deltaTime) {
|
|||||||
|
|
||||||
// Limitar magnitud de fuerza (evitar explosiones numéricas)
|
// Limitar magnitud de fuerza (evitar explosiones numéricas)
|
||||||
float force_magnitude = sqrtf(total_force_x * total_force_x + total_force_y * total_force_y);
|
float force_magnitude = sqrtf(total_force_x * total_force_x + total_force_y * total_force_y);
|
||||||
if (force_magnitude > ROTOBALL_MAX_FORCE) {
|
if (force_magnitude > max_force) {
|
||||||
float scale = ROTOBALL_MAX_FORCE / force_magnitude;
|
float scale_limit = max_force / force_magnitude;
|
||||||
total_force_x *= scale;
|
total_force_x *= scale_limit;
|
||||||
total_force_y *= scale;
|
total_force_y *= scale_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aplicar aceleración (F = ma, asumiendo m = 1 para simplificar)
|
// Aplicar aceleración (F = ma, asumiendo m = 1 para simplificar)
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ class Ball {
|
|||||||
// Cambia la gravedad
|
// Cambia la gravedad
|
||||||
void switchGravity();
|
void switchGravity();
|
||||||
|
|
||||||
|
// Reactiva la gravedad si está desactivada
|
||||||
|
void enableGravityIfDisabled();
|
||||||
|
|
||||||
|
// Fuerza gravedad ON (siempre activa)
|
||||||
|
void forceGravityOn();
|
||||||
|
|
||||||
|
// Fuerza gravedad OFF (siempre desactiva)
|
||||||
|
void forceGravityOff();
|
||||||
|
|
||||||
// Cambia la direcci\u00f3n de gravedad
|
// Cambia la direcci\u00f3n de gravedad
|
||||||
void setGravityDirection(GravityDirection direction);
|
void setGravityDirection(GravityDirection direction);
|
||||||
|
|
||||||
@@ -76,5 +85,5 @@ class Ball {
|
|||||||
|
|
||||||
// Sistema de atracción física hacia esfera RotoBall
|
// Sistema de atracción física hacia esfera RotoBall
|
||||||
void enableRotoBallAttraction(bool enable);
|
void enableRotoBallAttraction(bool enable);
|
||||||
void applyRotoBallForce(float target_x, float target_y, float deltaTime);
|
void applyRotoBallForce(float target_x, float target_y, float sphere_radius, float deltaTime);
|
||||||
};
|
};
|
||||||
@@ -65,7 +65,7 @@ enum class SimulationMode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Configuración de RotoBall (esfera 3D rotante)
|
// Configuración de RotoBall (esfera 3D rotante)
|
||||||
constexpr float ROTOBALL_RADIUS = 80.0f; // Radio de la esfera (píxeles)
|
constexpr float ROTOBALL_RADIUS_FACTOR = 0.333f; // Radio como proporción de altura de pantalla (80/240 ≈ 0.333)
|
||||||
constexpr float ROTOBALL_ROTATION_SPEED_Y = 1.5f; // Velocidad rotación eje Y (rad/s)
|
constexpr float ROTOBALL_ROTATION_SPEED_Y = 1.5f; // Velocidad rotación eje Y (rad/s)
|
||||||
constexpr float ROTOBALL_ROTATION_SPEED_X = 0.8f; // Velocidad rotación eje X (rad/s)
|
constexpr float ROTOBALL_ROTATION_SPEED_X = 0.8f; // Velocidad rotación eje X (rad/s)
|
||||||
constexpr float ROTOBALL_TRANSITION_TIME = 1.5f; // Tiempo de transición (segundos)
|
constexpr float ROTOBALL_TRANSITION_TIME = 1.5f; // Tiempo de transición (segundos)
|
||||||
|
|||||||
@@ -180,23 +180,52 @@ void Engine::handleEvents() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_G:
|
case SDLK_G:
|
||||||
switchBallsGravity();
|
// Si estamos en RotoBall, salir a modo física SIN GRAVEDAD
|
||||||
|
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||||
|
toggleRotoBallMode(false); // Desactivar RotoBall sin forzar gravedad ON
|
||||||
|
} else {
|
||||||
|
switchBallsGravity(); // Toggle normal en modo física
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Controles de dirección de gravedad con teclas de cursor
|
// Controles de dirección de gravedad con teclas de cursor
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
|
// Si estamos en RotoBall, salir a modo física CON gravedad
|
||||||
|
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||||
|
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
||||||
|
} else {
|
||||||
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
|
}
|
||||||
changeGravityDirection(GravityDirection::UP);
|
changeGravityDirection(GravityDirection::UP);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
|
// Si estamos en RotoBall, salir a modo física CON gravedad
|
||||||
|
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||||
|
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
||||||
|
} else {
|
||||||
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
|
}
|
||||||
changeGravityDirection(GravityDirection::DOWN);
|
changeGravityDirection(GravityDirection::DOWN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
|
// Si estamos en RotoBall, salir a modo física CON gravedad
|
||||||
|
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||||
|
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
||||||
|
} else {
|
||||||
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
|
}
|
||||||
changeGravityDirection(GravityDirection::LEFT);
|
changeGravityDirection(GravityDirection::LEFT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
|
// Si estamos en RotoBall, salir a modo física CON gravedad
|
||||||
|
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||||
|
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
||||||
|
} else {
|
||||||
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
|
}
|
||||||
changeGravityDirection(GravityDirection::RIGHT);
|
changeGravityDirection(GravityDirection::RIGHT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -441,6 +470,13 @@ void Engine::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::initBalls(int value) {
|
void Engine::initBalls(int value) {
|
||||||
|
// Si estamos en modo RotoBall, desactivarlo antes de regenerar pelotas
|
||||||
|
if (current_mode_ == SimulationMode::ROTOBALL) {
|
||||||
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
|
rotoball_.transitioning = false;
|
||||||
|
rotoball_.transition_progress = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Limpiar las bolas actuales
|
// Limpiar las bolas actuales
|
||||||
balls_.clear();
|
balls_.clear();
|
||||||
|
|
||||||
@@ -514,6 +550,24 @@ void Engine::switchBallsGravity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Engine::enableBallsGravityIfDisabled() {
|
||||||
|
for (auto &ball : balls_) {
|
||||||
|
ball->enableGravityIfDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::forceBallsGravityOn() {
|
||||||
|
for (auto &ball : balls_) {
|
||||||
|
ball->forceGravityOn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::forceBallsGravityOff() {
|
||||||
|
for (auto &ball : balls_) {
|
||||||
|
ball->forceGravityOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::changeGravityDirection(GravityDirection direction) {
|
void Engine::changeGravityDirection(GravityDirection direction) {
|
||||||
current_gravity_ = direction;
|
current_gravity_ = direction;
|
||||||
for (auto &ball : balls_) {
|
for (auto &ball : balls_) {
|
||||||
@@ -869,7 +923,7 @@ void Engine::performRandomRestart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sistema RotoBall - Alternar entre modo física y esfera 3D
|
// Sistema RotoBall - Alternar entre modo física y esfera 3D
|
||||||
void Engine::toggleRotoBallMode() {
|
void Engine::toggleRotoBallMode(bool force_gravity_on_exit) {
|
||||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||||
// Cambiar a modo RotoBall
|
// Cambiar a modo RotoBall
|
||||||
current_mode_ = SimulationMode::ROTOBALL;
|
current_mode_ = SimulationMode::ROTOBALL;
|
||||||
@@ -878,6 +932,9 @@ void Engine::toggleRotoBallMode() {
|
|||||||
rotoball_.angle_y = 0.0f;
|
rotoball_.angle_y = 0.0f;
|
||||||
rotoball_.angle_x = 0.0f;
|
rotoball_.angle_x = 0.0f;
|
||||||
|
|
||||||
|
// Desactivar gravedad al entrar en modo figura
|
||||||
|
forceBallsGravityOff();
|
||||||
|
|
||||||
// Generar esfera 3D
|
// Generar esfera 3D
|
||||||
generateRotoBallSphere();
|
generateRotoBallSphere();
|
||||||
|
|
||||||
@@ -904,6 +961,11 @@ void Engine::toggleRotoBallMode() {
|
|||||||
ball->enableRotoBallAttraction(false);
|
ball->enableRotoBallAttraction(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Activar gravedad al salir (solo si se especifica)
|
||||||
|
if (force_gravity_on_exit) {
|
||||||
|
forceBallsGravityOn();
|
||||||
|
}
|
||||||
|
|
||||||
// Mostrar texto informativo
|
// Mostrar texto informativo
|
||||||
text_ = "MODO FISICA";
|
text_ = "MODO FISICA";
|
||||||
int text_width = static_cast<int>(text_.length() * 8);
|
int text_width = static_cast<int>(text_.length() * 8);
|
||||||
@@ -918,6 +980,9 @@ void Engine::generateRotoBallSphere() {
|
|||||||
int num_points = static_cast<int>(balls_.size());
|
int num_points = static_cast<int>(balls_.size());
|
||||||
if (num_points == 0) return;
|
if (num_points == 0) return;
|
||||||
|
|
||||||
|
// Calcular radio dinámico proporcional a la altura de pantalla
|
||||||
|
float radius = current_screen_height_ * ROTOBALL_RADIUS_FACTOR;
|
||||||
|
|
||||||
// Constante Golden Ratio para Fibonacci sphere
|
// Constante Golden Ratio para Fibonacci sphere
|
||||||
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
||||||
const float angle_increment = PI * 2.0f * golden_ratio;
|
const float angle_increment = PI * 2.0f * golden_ratio;
|
||||||
@@ -929,9 +994,9 @@ void Engine::generateRotoBallSphere() {
|
|||||||
float theta = angle_increment * static_cast<float>(i); // Longitud
|
float theta = angle_increment * static_cast<float>(i); // Longitud
|
||||||
|
|
||||||
// Convertir coordenadas esféricas a cartesianas
|
// Convertir coordenadas esféricas a cartesianas
|
||||||
float x = cosf(theta) * sinf(phi) * ROTOBALL_RADIUS;
|
float x = cosf(theta) * sinf(phi) * radius;
|
||||||
float y = sinf(theta) * sinf(phi) * ROTOBALL_RADIUS;
|
float y = sinf(theta) * sinf(phi) * radius;
|
||||||
float z = cosf(phi) * ROTOBALL_RADIUS;
|
float z = cosf(phi) * radius;
|
||||||
|
|
||||||
// Guardar posición 3D en la pelota
|
// Guardar posición 3D en la pelota
|
||||||
balls_[i]->setRotoBallPosition3D(x, y, z);
|
balls_[i]->setRotoBallPosition3D(x, y, z);
|
||||||
@@ -942,7 +1007,7 @@ void Engine::generateRotoBallSphere() {
|
|||||||
balls_[i]->setRotoBallTarget2D(center_x + x, center_y + y);
|
balls_[i]->setRotoBallTarget2D(center_x + x, center_y + y);
|
||||||
|
|
||||||
// Calcular brillo inicial según profundidad Z
|
// Calcular brillo inicial según profundidad Z
|
||||||
float z_normalized = (z + ROTOBALL_RADIUS) / (2.0f * ROTOBALL_RADIUS);
|
float z_normalized = (z + radius) / (2.0f * radius);
|
||||||
balls_[i]->setDepthBrightness(z_normalized);
|
balls_[i]->setDepthBrightness(z_normalized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -951,6 +1016,9 @@ void Engine::generateRotoBallSphere() {
|
|||||||
void Engine::updateRotoBall() {
|
void Engine::updateRotoBall() {
|
||||||
if (current_mode_ != SimulationMode::ROTOBALL) return;
|
if (current_mode_ != SimulationMode::ROTOBALL) return;
|
||||||
|
|
||||||
|
// Calcular radio dinámico proporcional a la altura de pantalla
|
||||||
|
float radius = current_screen_height_ * ROTOBALL_RADIUS_FACTOR;
|
||||||
|
|
||||||
// Actualizar ángulos de rotación de la esfera
|
// Actualizar ángulos de rotación de la esfera
|
||||||
rotoball_.angle_y += ROTOBALL_ROTATION_SPEED_Y * delta_time_;
|
rotoball_.angle_y += ROTOBALL_ROTATION_SPEED_Y * delta_time_;
|
||||||
rotoball_.angle_x += ROTOBALL_ROTATION_SPEED_X * delta_time_;
|
rotoball_.angle_x += ROTOBALL_ROTATION_SPEED_X * delta_time_;
|
||||||
@@ -970,9 +1038,9 @@ void Engine::updateRotoBall() {
|
|||||||
float phi = acosf(1.0f - 2.0f * t);
|
float phi = acosf(1.0f - 2.0f * t);
|
||||||
float theta = angle_increment * static_cast<float>(i);
|
float theta = angle_increment * static_cast<float>(i);
|
||||||
|
|
||||||
float x = cosf(theta) * sinf(phi) * ROTOBALL_RADIUS;
|
float x = cosf(theta) * sinf(phi) * radius;
|
||||||
float y = sinf(theta) * sinf(phi) * ROTOBALL_RADIUS;
|
float y = sinf(theta) * sinf(phi) * radius;
|
||||||
float z = cosf(phi) * ROTOBALL_RADIUS;
|
float z = cosf(phi) * radius;
|
||||||
|
|
||||||
// Aplicar rotación en eje Y
|
// Aplicar rotación en eje Y
|
||||||
float cos_y = cosf(rotoball_.angle_y);
|
float cos_y = cosf(rotoball_.angle_y);
|
||||||
@@ -991,10 +1059,10 @@ void Engine::updateRotoBall() {
|
|||||||
float target_y = center_y + y_rot;
|
float target_y = center_y + y_rot;
|
||||||
|
|
||||||
// Aplicar fuerza de atracción física hacia el punto rotado
|
// Aplicar fuerza de atracción física hacia el punto rotado
|
||||||
balls_[i]->applyRotoBallForce(target_x, target_y, delta_time_);
|
balls_[i]->applyRotoBallForce(target_x, target_y, radius, delta_time_);
|
||||||
|
|
||||||
// Calcular brillo según profundidad Z para renderizado
|
// Calcular brillo según profundidad Z para renderizado
|
||||||
float z_normalized = (z_final + ROTOBALL_RADIUS) / (2.0f * ROTOBALL_RADIUS);
|
float z_normalized = (z_final + radius) / (2.0f * radius);
|
||||||
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
||||||
balls_[i]->setDepthBrightness(z_normalized);
|
balls_[i]->setDepthBrightness(z_normalized);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ private:
|
|||||||
void setText();
|
void setText();
|
||||||
void pushBallsAwayFromGravity();
|
void pushBallsAwayFromGravity();
|
||||||
void switchBallsGravity();
|
void switchBallsGravity();
|
||||||
|
void enableBallsGravityIfDisabled();
|
||||||
|
void forceBallsGravityOn();
|
||||||
|
void forceBallsGravityOff();
|
||||||
void changeGravityDirection(GravityDirection direction);
|
void changeGravityDirection(GravityDirection direction);
|
||||||
void toggleVSync();
|
void toggleVSync();
|
||||||
void toggleFullscreen();
|
void toggleFullscreen();
|
||||||
@@ -125,7 +128,7 @@ private:
|
|||||||
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);
|
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);
|
||||||
|
|
||||||
// Sistema RotoBall
|
// Sistema RotoBall
|
||||||
void toggleRotoBallMode();
|
void toggleRotoBallMode(bool force_gravity_on_exit = true);
|
||||||
void generateRotoBallSphere();
|
void generateRotoBallSphere();
|
||||||
void updateRotoBall();
|
void updateRotoBall();
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user