Meta Description" name="description" />
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>SimRacing GT</title>
<script>
// ==============================================
// 🛞 FÍSICA NEUMÁTICOS - FÓRMULA PACEJKA (IGUAL QUE GRAN TURISMO)
// ==============================================
class Neumatico {
constructor() {
this.B = 14.8;
this.C = 1.62;
this.D = 1.78;
this.E = -0.28;
this.temperatura = 85;
this.presion = 2.2;
this.desgaste = 0;
this.mojado = false;
}
calcularFuerzaLateral(anguloDeslizamiento, cargaSobreRueda) {
const factorTemp = Math.max(0, 1 - Math.abs(this.temperatura - 90)/80);
const factorPiso = this.mojado ? 0.65 : 1;
const factorDesgaste = Math.max(0, 1 - this.desgaste / 180);
let fuerza = cargaSobreRueda * this.D * Math.sin(
this.C * Math.atan(
this.B * anguloDeslizamiento - this.E * (this.B * anguloDeslizamiento - Math.atan(this.B * anguloDeslizamiento))
)
);
return fuerza * factorTemp * factorPiso * factorDesgaste;
}
calcularFuerzaLongitudinal(deslizamiento, carga) {
return carga * this.D * 0.9 * Math.sin(this.C * Math.atan(this.B * deslizamiento * 0.75)) * (this.mojado ? 0.7 : 1);
}
}
// ==============================================
// ⚙️ DINÁMICA COMPLETA VEHÍCULO GT3
// ==============================================
class VehiculoGT3 {
constructor(gl) {
this.gl = gl;
this.masa = 1245;
this.distanciaEjes = 2.75;
this.alturaCentroGravedad = 0.31;
this.distribucionPeso = 0.525;
this.coefSustentacion = -3.2;
this.coefResistencia = 0.305;
this.anguloAleron = 22;
this.ruedas = [new Neumatico(), new Neumatico(), new Neumatico(), new Neumatico()];
this.pos = {x:0, y:0.25, z:0};
this.vel = {x:0, y:0, z:0};
this.acel = {x:0, y:0, z:0};
this.rot = {y:0};
this.vbos = {};
this.crearGeometria();
}
crearGeometria() {
const gl = this.gl;
this.suelo = new Float32Array([-500,0,-500, 500,0,-500, 500,0,500, -500,0,500]);
this.vbos.suelo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbos.suelo);
gl.bufferData(gl.ARRAY_BUFFER, this.suelo, gl.STATIC_DRAW);
this.chasis = new Float32Array([
-1.1,0.2,-2, 1.1,0.2,-2, 1.1,0.2,1.6, -1.1,0.2,1.6,
-1.1,0.6,-1.7, 1.1,0.6,-1.7, 1.1,0.6,1.1, -1.1,0.6,1.1
]);
this.vbos.chasis = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbos.chasis);
gl.bufferData(gl.ARRAY_BUFFER, this.chasis, gl.STATIC_DRAW);
}
actualizar(volante, acelerador, dt) {
const gravedad = 9.81;
const velocidad = Math.hypot(this.vel.x, this.vel.z);
const cargaAire = 0.6 * 1.225 * velocidad * velocidad * this.coefSustentacion * (1 + this.anguloAleron / 45);
const fResistencia = -0.5 * 1.225 * velocidad * velocidad * this.coefResistencia * Math.sign(velocidad || 0.001);
const transFrenada = this.masa * this.acel.z / this.distanciaEjes * this.alturaCentroGravedad;
const transCurva = this.masa * this.acel.x / this.distanciaEjes * this.alturaCentroGravedad * 1.3;
const cargas = [
(this.masa*gravedad*this.distribucionPeso/2) - transFrenada/2 + transCurva,
(this.masa*gravedad*this.distribucionPeso/2) - transFrenada/2 - transCurva,
(this.masa*gravedad*(1-this.distribucionPeso)/2) + transFrenada/2 + transCurva,
(this.masa*gravedad*(1-this.distribucionPeso)/2) + transFrenada/2 - transCurva
];
let fX = 0, fZ = 0;
for(let i=0; i<4; i++) {
const angulo = i<2 ? volante * 0.18 : 0;
const fLat = this.ruedas[i].calcularFuerzaLateral(angulo, cargas[i] + cargaAire/4);
const fLon = this.ruedas[i].calcularFuerzaLongitudinal(acelerador * 0.25, cargas[i] + cargaAire/4);
fX += fLat * Math.cos(this.rot.y) - fLon * Math.sin(this.rot.y);
fZ += fLat * Math.sin(this.rot.y) + fLon * Math.cos(this.rot.y);
}
fZ += fResistencia;
this.acel.x = fX / this.masa;
this.acel.z = fZ / this.masa;
this.vel.x += this.acel.x * dt;
this.vel.z += this.acel.z * dt;
this.pos.x += this.vel.x * dt;
this.pos.z += this.vel.z * dt;
this.rot.y += volante * velocidad * 0.022 * dt / Math.max(0.5, velocidad+0.3);
}
dibujar(gl) {
gl.clearColor(0.08, 0.14, 0.22, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbos.suelo);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbos.chasis);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
gl.drawArrays(gl.TRIANGLE_FAN, 4, 4);
}
}
// ==============================================
// 🎮 CONTROLES OPTIMIZADOS IPHONE - GIROSCOPIO
// ==============================================
let volante = 0;
let acelerar = 0;
let frenar = 0;
const sensibilidad = 2.8;
const suavizado = 0.12;
if(DeviceOrientationEvent?.requestPermission) {
DeviceOrientationEvent.requestPermission().then(permiso => {
if(permiso === 'granted') {
window.addEventListener('deviceorientation', e => {
const entrada = Math.max(-1, Math.min(1, e.gamma / 38 * sensibilidad));
volante = volante * (1-suavizado) + entrada * suavizado;
});
}
});
}
document.addEventListener('touchstart', e => {
const mitad = innerWidth /2;
for(let t of e.touches) {
if(t.pageX > mitad) acelerar = Math.min(1, (innerHeight - t.pageY)/420);
else frenar = Math.min(1, (innerHeight - t.pageY)/420);
}
});
document.addEventListener('touchend', () => { acelerar=0; frenar=0; });
// ==============================================
// 🚀 ARRANQUE PRINCIPAL
// ==============================================
window.onload = () => {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { powerPreference:'high-performance', antialias:true, depth:true });
document.body.appendChild(canvas);
function ajustar() {
canvas.width = innerWidth * devicePixelRatio;
canvas.height = innerHeight * devicePixelRatio;
gl.viewport(0,0,canvas.width,canvas.height);
}
ajustar();
addEventListener('resize', ajustar);
const coche = new VehiculoGT3(gl);
let tAnt = performance.now();
function bucle() {
const ahora = performance.now();
const dt = Math.min(0.016, (ahora - tAnt)/1000);
tAnt = ahora;
coche.actualizar(volante, acelerar - frenar, dt);
coche.dibujar(gl);
requestAnimationFrame(bucle);
}
bucle();
}
</script>
<style>*{margin:0;padding:0;box-sizing:border-box;overflow:hidden;}html,body{width:100%;height:100%;background:#000;}canvas{display:block;position:absolute;inset:0;}</style>
</head>
<body></body>
</html>1
1
8KB
8KB
106.0ms
304.0ms
107.0ms