Lanzamiento muy pronto · Regalamos 100 licencias a los 100 primeros100 licencias gratisApúntate
EN
← Volver a la portada

Cómo se generan las pistas: las reglas de realismo

Documento de trabajo del generador, publicado tal cual. Es técnico y largo: está aquí para quien quiera saber exactamente qué se hace y por qué.

Objetivo: que al seleccionar cualquier zona del mapa salga una pista de Assetto Corsa lo más parecida posible a la realidad, con detalle a pie de coche. Este documento explica el método, qué datos usamos para cada elemento, cómo se elige el “modelo” que lo representa y cómo se valida. Es un documento vivo: cada iteración con pruebas en el juego lo actualiza.

1. Principio: realidad > embellecimiento, pero con el nivel de detalle de un videojuego

Cada objeto del mundo se reconstruye con esta cadena, siempre en este orden:

  1. Posición y forma reales (datos abiertos): OSM, Catastro, DEM del IGN/Copernicus, ortofoto PNOA, cobertura terrestre WorldCover, altura de dosel (Meta/WRI), detecciones de Mapillary. Si el dato existe, se usa tal cual.
  2. Atributos reales cuando existen (altura, plantas, uso, año, color, material, anchura, carriles, superficie, nombre, velocidad máxima…). Se guarda la confianza (REAL_DATA / HIGH_CONFIDENCE / ESTIMATED).
  3. Modelo o material “de biblioteca” elegido por esos atributos: no inventamos la forma, sólo el aspecto de cerca (textura, relieve, ventanas, mobiliario) con reglas que dependen de los atributos: un edificio marrón de 6 plantas de 1975 recibe una fachada de bloque de los 70 con enlucido marrón y persianas; una nave industrial de 2005 recibe chapa y portones; una rotonda de 30 m recibe isleta elevada con bordillo, césped y palmeras.
  4. Física de AC coherente (nombres 1ROAD_, 1KERB_, 1WALL_, 1SAND_…), LOD y chunking para 400 fps.
  5. Validación: validador propio, render en Blender, y prueba en el juego (log de CSP para luces/shaders).

2. La “biblioteca de recursos”: procedural + fotografías CC0 + modelos del propio AC

Se valoraron bibliotecas de modelos descargables (Kenney, Quaternius, Poly Haven, Sketchfab CC-BY). Conclusión: no hay bibliotecas libres de edificios realistas que encajen con huellas reales de cualquier ciudad; los modelos fijos no se adaptan a la forma ni al tamaño exacto. Por eso la biblioteca de WorldLaps es paramétrica:

ElementoDato realBiblioteca / regla de aspecto
Edificiohuella Catastro/OSM, plantas, uso, año, building:colour/material, roof:shapegramática de fachadas: paleta por época y región × tipo de ventana × balcón/persiana × planta baja (portal, comercios con rótulo real, garaje) × cornisa/pretil; texturas CC0 de enlucido/ladrillo/chapa; tejado con la ortofoto real
Calleeje, anchura, carriles, surface, lane_markings, cruces, cyclewayasfalto multicapa estilo Kunos (foto + grano + normal + fresnel), adoquín CC0, marcas viales, cebras en su sitio
Acerasidewalk, anchurabaldosa fotográfica CC0 + normal, bordillo de granito
Rotondajunction=roundabout (radio real)isleta elevada con bordillo, césped/pavimento según tamaño, vegetación o elemento central, señal de rotonda
FarolaOSM street_lamp + detecciones Mapillary + relleno cada 35 mposte + brazo + luminaria emisiva + luz CSP nocturna
SeñalOSM traffic_sign/maxspeed + Mapillary (tipo real)placa con el pictograma/valor real
Vegetacióndosel 1 m (posición y altura reales), OSM (especie), WorldCover (densidad)billboards por especie (pino, olivo, ciprés, palmera, frondosa)
MobiliarioOSM + Mapillarymarquesinas, bancos, papeleras, bolardos, buzones, hidrantes, armarios
Estructuras altasOSM man_made con alturachimeneas y depósitos como cilindros, torres de alta tensión con cables, mástiles, grúas
Vallas y murosOSM barrier + límites de parcelas industrialesvalla metálica, muro, seto, guardarraíl
Mar/costanatural=coastline, WorldCoverplano de agua a nivel del mar, escollera con bloques de roca y corona de hormigón, muelles con cantil
Coches aparcadosaparcamientos OSM (amenity=parking)modelos LOD de los coches de Kunos instalados (uso personal) o cajas procedurales
HorizonteCopernicus 30 m + PNOA 4–8 m/px + edificios OSM en 1,5 kmanillo de terreno + edificios lejanos

3. Cómo se decide el aspecto de un edificio (detalle)

  1. Huella y altura: Catastro (numberOfFloorsAboveGround, partes) o OSM (height, building:levels).
  2. Uso (Catastro currentUse): residencial / industrial / oficinas / comercio / público / agrícola.
  3. Época (Catastro dateOfConstruction): < 1950 tradicional (enlucido cálido, ventanas altas con persiana de lamas, balcones de forja), 1950–1990 bloques (ladrillo visto o enlucido gris/ocre, persianas enrollables, balcones corridos), > 1990 moderno (tonos claros, ventanas apaisadas, barandillas de aluminio/vidrio).
  4. Color/material: building:colour / building:material si existen; si no, paleta regional (Mediterráneo: blancos, cremas, ocres, terracota, grises) con semilla estable por edificio para que cada uno sea distinto pero coherente.
  5. Planta baja: portal + comercios (rótulo con el nombre real del negocio si OSM lo tiene) en calles comerciales; garajes/portales en residenciales; portones en naves.
  6. Cubierta: ortofoto real (tejas, terrazas, placas solares se ven tal cual) + roof:shape cuando está mapeado; pretiles, casetones de escalera y antenas procedurales según época.
  7. Detalles 3D cerca de la ruta: balcones, aleros y toldos como geometría; lejos, todo en textura (LOD).

3b. Catálogo de elementos: qué se genera para cada cosa que hay en el mapa

Elemento del mundoDetección (dato real)Recurso generadoEstado
Calle / avenidaOSM highway + corredor entre fachadas (Catastro)asfalto multicapa con anchura real, bombeo, marcas, cebras, carriles bici, badenes, aparcamiento en línea✅
Rotondajunction=roundaboutisleta con bordillo, césped/pavimento, palmeras/olivos, pedestal✅
Puente / túnelbridge, tunnel, layertablero con pretiles / tubo✅
Aparcamientoamenity=parking (superficie)plazas pintadas (líneas blancas y topes de rueda) con coches reales de Kunos✅
Parking subterráneo / garajeparking_entrance, entrance=garagehueco con rampa en la fachada✅
EdificioCatastro/OSM (huella, plantas, uso, año, color)gramática de 24 fachadas, rótulos reales, tejado con ortofoto✅
Nave industrial / depósito / silo / chimeneaCatastro uso industrial, man_madechapa + portones + rótulo de empresa; cilindros✅
Parque / jardínleisure=park/gardensenderos reales pavimentados, césped, árboles reales (dosel), bancos, papeleras, farolas✅
Parque infantilleisure=playgroundvalla, suelo de caucho, columpio, tobogán✅
Pista deportiva (fútbol, básket, tenis, pádel)leisure=pitch + sportvalla alta, porterías/canastas, líneas de tenis/pádel, red, paredes de vidrio (pádel)✅
Playanatural=beach, WorldCoverarena (textura suave, física SAND)✅
Marnatural=coastlineplano de agua a nivel del mar✅
Puerto marítimoman_made=pier/breakwater/quay, crane, landuse=portescollera de rocas + corona, muelles con cantil, grúas pórtico/torre, naves, vallas de parcela✅ (edificio de entrada/garita y contenedores: 🔜)
Río / arroyo / canalwaterway=river/stream/canal (+width)cinta de agua a la cota del cauce✅
Cascadawaterway=waterfallcortina de agua vertical🔜
Montaña / colinaDEM 5 m + Copernicus (horizonte)relieve real; laderas > 30° con roca/tierra en vez de hierba; ortofoto✅
Bosque / cultivo / matorralWorldCover + OSM landuse + dosel 1 márboles fotográficos con posición y altura reales, arbustos✅
Vías de trenrailwayraíles + balasto✅
Torres eléctricaspower=tower/linepilonas con cables en catenaria✅
Farolas, señales, semáforos, paradas, bancos, papeleras, bolardosOSM + Mapillarymodelos procedurales, luz CSP nocturna, nombres reales✅
Vallas, muros, setos, guardarraílesOSM barrier + bordes de parcela industrialcintas por tipo✅
Coches aparcadosaparcamientos y calles (OSM)modelos LOD de Kunos con pintura✅
Gasolineraamenity=fuelmarquesina + surtidores🔜
Estación de tren / andénrailway=station/platformandén elevado + marquesina🔜
Cementerio, iglesia, castillolanduse=cemetery, building=church/castlemuros, torre/campanario, almenas🔜

3c. Biblioteca de modelos: la lista de lo que hace falta para cualquier zona del mapa

Regla: primero se piensa qué elementos puede tener cualquier área (ciudad, costa, campo, montaña, industria) y para cada uno se decide si el recurso es (a) procedural desde el dato (edificios, carreteras, terreno), (b) un modelo de biblioteca instanciado (mobiliario, equipamiento), (c) un modelo del propio AC del usuario (coches, árboles) o (d) una receta paramétrica (monumentos). Los modelos de biblioteca se hacen con Blender headless (scripts/assetkit: un guion por modelo, materiales fotográficos CC0 con desgaste, horneado a texturas propias; Poly Haven como fuente cuando tiene el objeto) y se exportan a assets/models/<nombre>.obj + tex/<nombre>_*.jpg (Y arriba, origen en el suelo, metros, material baked = su propia textura, que mesh/atlas.py empaqueta por pista) y mesh/assetlib.py los carga e instancia (posición, giro, escala, fusión por material, colisión opcional 1WALL_).

Convención de ejes (verificada): Blender +Z → +Y, Blender +X → +X, Blender +Y → −Z. yaw_facing orienta el frente (+Z) y yaw_along el lado largo o brazo (+X).

GrupoElementoRecursoDónde se usaEstado
Mobiliario urbanobanco (madera + fundición)bench.objamenity=bench, parques✅
papeleralitter_bin.objamenity=waste_basket✅
contenedor de basura / reciclajedumpster.objamenity=recycling, waste_disposal✅
marquesina de autobús (vidrio, asientos, panel)bus_shelter.obj + poste con nombre realhighway=bus_stop (shelter≠no)✅
farola con brazo y luminaria emisivastreet_lamp.objOSM highway=street_lamp + Mapillary + inferidas✅
semáforo (3 lentes, viseras)traffic_light.objhighway=traffic_signals✅
bolardobollard.objbarrier=bollard✅
jardinera de hormigónplanter.objplazas peatonales (highway=pedestrian + area=yes, place=square) cada 18 m✅
mesa de picnicpicnic_table.objparques grandes (>300 m²), leisure=picnic_table✅
señales de tráfico (placa + poste)atlas procedural con códigos realesOSM / Mapillary✅
buzón, hidrante, armario, mástilprimitivasOSM amenity/emergency/man_made✅
Deporte / ocioportería de fútbol con redgoal.objsport=soccer/multi✅
canasta con tablerohoop.objsport=basketball✅
columpio, tobogánswing.obj, slide.objleisure=playground✅
red y líneas de tenis/pádel, vidrio de pádelproceduralsport=tennis/padel✅
Serviciosgasolinera: marquesina + 3 surtidoresfuel_canopy.obj, fuel_pump.objamenity=fuel (nodo o área)✅
Puerto / industriagarita de control con barreraport_booth.objbarrier=lift_gate/toll_booth✅
contenedor marítimo 40’container.objlanduse=port/harbour, industrial=port (pilas de 1-3 en la explanada, lejos de naves y viales)✅
grúas pórtico y torre, chimeneas, depósitos, silosprocedural desde man_madepuertos y polígonos✅
Vehículoscoches aparcadosLOD de Kunos (uso personal)parkings y bandas de aparcamiento reales✅
Vegetaciónárboles fotográficos, palmeras, setos, arbustosbillboards Kunos + procedurales, dosel 1 mWorldCover, OSM, canopy✅
Singularesalto horno, faro, campanario, castillo, molino, monumentorecetas paramétricas (Wikidata)mesh/landmarks.py✅
Mobiliario urbano (2ª tanda)cabina telefónica, quiosco, terraza (mesa+sillas+sombrilla), parada de taxi, valla publicitaria 8×3, mupi, columna publicitaria, aparcabicis, fuente ornamental, fuente de agua potable, máquina expendedora, parquímetro, cargador eléctrico, reloj de calle, pozo, radar, cámara CCTV, puesto de mercado, contenedor soterrado, salvavidasphone_booth, kiosk, terrace_set, taxi_sign, billboard, ad_board, ad_column, bike_rack, fountain, drinking_fountain, vending_machine, parking_meter, charging_station, street_clock, water_well, speed_camera, cctv_post, market_stall, underground_container, life_ring_postmesh/amenities.py (nodos OSM: amenity, shop, advertising, highway=speed_camera, man_made=surveillance, emergency, outdoor_seating=yes en nodos y edificios)✅
Transportemarquesina de andén, andén (extrusión), catenaria cada 50 m en vías electrificadas, tope de vía, barrera de paso a nivel, conos y vallas de obra en highway=construction, noray en muellesplatform_shelter, catenary_post, buffer_stop, crossing_barrier, traffic_cone, work_barrier, mooring_bollardmesh/zones.py, mesh/amenities.py✅
Costa y marboya/baliza (seamark:type), lancha y velero amarrados en leisure=marina, sombrillas y hamacas en playas grandes, torre de socorristabuoy, motorboat, sailboat, beach_umbrella, sunbed, lifeguard_towermesh/zones.py✅
Energía / industria / ruralaerogenerador (altura real de OSM), huerto solar (filas orientadas al sur), transformadores en subestaciones, tuberías aéreas (man_made=pipeline + location=overground), invernaderos en filas, pilas de contenedores en zonas portuariaswind_turbine, solar_panel, transformer, greenhouse, container + cinta tubularmesh/zones.py✅
Cementerios, ociolápidas y cruces en landuse=cemetery, refugio de picnic, balancín en parques infantiles, halfpipe en sport=skateboard, piscinas (leisure=swimming_pool) con agua, jardineras en plazas peatonalesgravestone, gravestone_cross, picnic_shelter, seesaw, halfpipe, plantermesh/zones.py, mesh/leisure.py✅
Vehículos (3ª tanda)autobús urbano, furgoneta, camión rígido, tráiler, scooter, bicicleta, tractor, caravana, carretilla elevadora, excavadoracity_bus, van, box_truck, semi_trailer, scooter, bicycle, tractor, caravan, forklift, excavatormesh/transport.py: estaciones de autobús, aparcamientos hgv=yes, polígonos industriales y puertos, motorcycle_parking, aparcabicis, granjas (landuse=farmyard), campings, canteras y obras✅
Ferrocarril y aéreolocomotora + 3 coches parados en el 55 % de los andenes, tranvía, señal ferroviaria, cartel de estación, pilona y cabina de teleférico / silla de telesilla con cable, avioneta, manga de viento, pistas y calles de rodaje (asfalto + eje), plataformatrain_loco, train_coach, tram, rail_signal, station_sign, cable_car_pylon, cable_car_cabin, chairlift_chair, airplane_small, windsockrailway=platform, aerialway=*, aeroway=runway/taxiway/apron✅
Costa (3ª tanda)barco pesquero en dársenas, pantalán flotante (floating=yes), moto acuática, chiringuito, ducha de playafishing_boat, pontoon, jet_ski, beach_bar, beach_showerharbour=yes, man_made=pier, amenity=shower✅ (moto acuática y chiringuito: modelo listo, sin etiqueta fiable)
Carretera (4ª tanda)pórtico de señalización en autopista cada 1,5 km, banderola, captafaros cada 50 m en carreteras sin acera, hito kilométrico, semáforo peatonal en cruces semaforizados, peaje, tope de plaza, toldo de comercio, cubo de basurasign_gantry, cantilever_sign, delineator, km_post, pedestrian_signal, toll_canopy, wheel_stop, shop_awning, wheelie_binmesh/zones.py, mesh/amenities.py, mesh/shopsigns.py (toldos sobre bares, panaderías, tiendas)✅
Pequeñas estructurastemplete, aseos públicos, centro de transformación, cajero, cruz de término, cruz de cumbre, vértice geodésico en natural=peak, panel informativo y mapa, poste indicador de senderos, barbacoa, ermita/hornacina, pérgola, tienda de campaña, cabañabandstand, public_toilets, substation_kiosk, atm, wayside_cross, summit_cross, survey_point, info_board, guidepost, bbq, shrine, pergola, tent, hutnodos OSM (amenity, historic, man_made, tourism=information/viewpoint)✅
Deporte (4ª tanda)gradas con cubierta a lo largo de estadios, torres de iluminación en estadios y campos grandes, gimnasio al aire libre, mesa de ping-pong, muelle infantil, estructura de escalada, red de voleibolgrandstand, floodlight_mast, outdoor_gym, table_tennis, spring_rider, climbing_frame, volleyball_netleisure=stadium/pitch/playground/fitness_station✅
Rural e industria (4ª tanda)puerta de finca en barrier=gate, paso canadiense, colmena, pacas de heno (granjas y cereal), abrevadero, rocas en roquedo, torre de refrigeración (escala real), decantador de depuradora, acopio de árido, cascada (cortina escalada al desnivel del MDT), pasarela de madera y paseos entarimados (surface=wood)farm_gate, cattle_grid, beehive, hay_bale, water_trough, boulder, cooling_tower, clarifier_tank, stockpile, waterfall, footbridgemesh/amenities.py, mesh/zones.py, mesh/transport.py✅
Azoteascasetón de escalera, depósitos de agua, aires acondicionados, antenas de TV, parabólicas, calentadores solares, chimeneas sobre cubiertas planas (Catastro/OSM) a menos de 220 m del recorridoroof_stair_box, roof_tank, roof_ac, roof_antenna, roof_dish, roof_solar_heater, roof_chimneymesh/roofs.py✅
Grandes vehículos (5ª tanda)avión de línea en aprons de aeropuertos con pista > 1,8 km, helicóptero en helipuertos, ferry en terminales (amenity=ferry_terminal), carguero portacontenedores y ferry amarrados en los muelles al agua de puertos comerciales, estatua ecuestre / figura para monumentos tipo estatua (Wikidata/OSM)airliner, helicopter, ferry, cargo_ship, statue_equestrian, statuemesh/transport.py, mesh/landmarks.py✅
Detalle a pie de calle (6ª tanda)tapas de alcantarilla (nodos OSM manhole=* o cada 40 m en calles urbanas cerca del recorrido), imbornales en ambos bordillos cada 28 m, tótem de precios en gasolineras, espejo de tráfico, contenedor de sal, poste SOS, poste de parada de bus con marquesina de horariosmanhole, storm_drain, fuel_price_sign, traffic_mirror, grit_bin, emergency_phone, bus_stop_polemesh/zones.py, mesh/amenities.py, mesh/street.py✅
Fachadas en 3D (7ª tanda)balcones de forja (estilos tradicionales y bloques) o de vidrio (modernos) exactamente en las ventanas que pinta la textura (una por módulo de 3 m, plantas alineadas al alero), tendederos en el 12 %, aires acondicionados de ventana y toldos en otras ventanas; sólo en paredes expuestas (no medianeras) a menos de 130 m del recorridobalcony, balcony_glass, clothes_line, window_ac_unit, window_awningmesh/balconies.py (usa las fachadas registradas por build_buildings)✅
Pendientesla forma exacta de cada monumento (Wikidata no da geometría); todo lo demás que OSM etiqueta tiene ya un modelo o una receta———

Cómo se añade un modelo nuevo: un guion scripts/assetkit/models/<nombre>.py con build() (geometría de kit, materiales de mats, o ph.load de un modelo de Poly Haven), python scripts/assetkit/build.py <nombre> (hornea sus texturas y deja la vista previa en cache/assetkit/preview/<nombre>.jpg: mirarla siempre), instanciar con Instancer.add("<nombre>", x, y, z, yaw) en el builder correspondiente y añadir la fila aquí (licencias en assets/LICENSES.md: CC0, hechos en este repositorio con material CC0).

3d. Reglas duras de generación (la carretera manda)

Estas reglas se aplican por construcción y se verifican al final del pipeline (mesh/rules.py, resultado en work/summary.json["rules"] y en el log como RULE R1/R2):

ReglaQué garantizaCómo
R1 Continuidad de la superficieEntre muestras consecutivas de 2,5 m (1,5 m en el tramo) a −3, 0 y +3 m del eje, el salto de altura nunca supera 0,25 mPerfil normalizado (mediana + media + limitador de curvatura por clase); cada punto de calzada toma la altura de la vía que lo contiene, interpolada a lo largo de ella (nunca de la otra rama de una horquilla ni de una calle lateral); las muestras finales de una calle lateral dentro de la calzada de una vía más ancha no definen altura; en cruces en T la vía que termina se adapta por completo a la que continúa con rampa suave de 30 m
R2 Nada sobre la calzadaNingún vértice de árbol, señal, farola, mobiliario, valla, edificio ni modelo de biblioteca dentro del asfalto entre la superficie y 6 m sobre ellaTodos los objetos se apartan del asfalto (offside, borde real del polígono); assetlib.KEEP_OUT rechaza cualquier instancia dentro del asfalto salvo los objetos que van sobre él (alcantarillas, imbornales, conos, barreras…); árboles filtrados por el polígono pavimentado; muros/vallas OSM recortados; edificios recortados; comprobación final que elimina lo que se cuele
R3 Anchura coherenteUna carretera tiene una sola anchura a lo largo de su recorrido; nunca se ensancha “hacia el vacío”; dos vías paralelas nunca se solapanCarreteras (motorway…tertiary) con anchura etiquetada; calles con mediana por way; corredor de fachadas solo con fachadas a ambos lados; _deconflict_parallel (la calle cede ante la carretera)
R4 Topología realCada cruce es extremo de segmento; rotondas como anillos cerrados; discos de asfalto solo en encuentros extremo-extremo_topology_pieces, discos de cruce
R5 Nada bloquea la salidaNingún coche u objeto físico a menos de 7 m de un box o plaza de parrilla ni sobre la pista diseñada / pit laneexclusiones en build_parked_cars

| R6 Puentes con gálibo | Un tablero es una recta entre sus extremos; si cruza otra vía (intersección real de ejes, no sus propios accesos) se eleva EN BLOQUE hasta dejar 4,8 m libres, se prolonga hasta cubrir toda la calzada cruzada (+1 m) y sus accesos suben en rampa ≤10 %; ni el tablero ni sus accesos se reconcilian nunca con la vía que pasa por debajo | _bridge_tunnel_profiles, _extend_decks_over_crossings, BRIDGE_CLEARANCE; guarda en _reconcile_junctions | | R8 Envolvente del tramo (modo tramo) | Nada invade la calzada del tramo (+0,5 m) entre −0,3 y +4,6 m sobre la superficie: tableros, pretiles, edificios, muros, árboles pierden esos triángulos; R1 se mide en el tramo con 0,15 m | rules.check_course_envelope, check_surface(way_ids=); summary.json["rules"]["course_*"] | | R9 El tramo manda | La carretera del tramo posee toda su SECCIÓN (calzada + acera o arcén + 1 m): ninguna muestra de otra vía define altura dentro de ella, y un tablero o túnel nunca define la superficie del suelo (su calzada es una malla aparte); cualquier muestra situada >2,5 m por encima de la vía que la contiene (acceso elevado, terraplén) tampoco, pesa más que cualquier otra vía en los cruces (las laterales se adaptan, nunca al revés) y su perfil se normaliza como UNA sola curva a lo largo de cada cadena de trozos OSM (r_min 450 m); las dos mitades de sentido único de una misma carretera conservan solo sus carriles | RoadNetwork.priority, _reconcile_junctions(priority=), _smooth_priority_chains, _deconflict_parallel | | R10 Pórticos y bancales | Un tablero corto (≤30 m) en zona edificada (≥3 edificios a 30 m) que cruza otra vía se modela como pórtico de mampostería con arco (arranque 4,6 m, clave 5,6 m sobre la vía inferior, pilares de 1,5 m, bloque = huella del tablero); en zonas edificadas el terreno junto a la calle se resuelve como bancal (talud corto ≤6 m) con muros de contención automáticos (piedra, físicos) donde el terreno a 3 m del bordillo está >1,2 m por encima o por debajo de la calle | mesh/arch.py::build_arch, RoadSeg.arch/lift/crossings, barriers.build_retaining_walls, ground.py (mezcla urbana) | | R12 Mapas grandes = franja | Un área de más de 40 km² avisa antes de generar; en modo tramo ofrece generar solo una franja de N m (500 por defecto) a cada lado del recorrido (POST /api/area/corridor). El terreno fino, la vegetación, los edificios y el mobiliario solo se generan dentro del polígono (+80/400 m); el resto del rectángulo lleva terreno real grueso (30 m) para no dejar huecos hasta el horizonte; las rejillas del terreno se construyen por franjas y la altura de árboles de Meta baja de resolución en áreas grandes. Una carretera pinchada que queda fuera del área se ignora con aviso. summary.json["request"] guarda polígono, carreteras y puntos de clic | geo/corridor.py, ground._grid_strips/_grid_in, ingest.py, route.ordered_stage_route | | R13 Foto aérea fina en franjas | Si el mosaico del área sale más grueso de lo que pide la calidad (una sola imagen sobre el rectángulo de una franja larga: 2-3 m/px), las teselas de terreno que tocan el área (+100 m) se piden de nuevo al proveedor a resolución completa (0,25 m/px en alta, 4096 px por tesela de 1 km); las de fuera se remuestrean del mosaico grueso. Se cargan de una en una (máscara, DDS y liberar) para no agotar la memoria; si una tesela fina falla se usa la gruesa | ortho_tiles.OrthoTiles(fine_*), pipeline._ortho_tiles | | R14 Salida y meta en la carretera | Un tramo puede empezar y acabar a mitad de una vía (stage_start/stage_end, lon/lat): se recorta el recorrido ahí. Se usa para no salir ni llegar por las calles escalonadas de un pueblo (salida y meta a 150 m de la última casa). El control R1 del tramo solo mide el tramo que se conduce | route.trim_route, rules.check_surface(near=) | | R15 En un puente solo hay tablero | El terreno bajo la huella de un tablero (+0,6 m) se baja 1 m por debajo de su superficie y solo puede subir 1,2 m por metro fuera del borde (el MDT de 5 m suaviza los barrancos y la ladera atravesaba el puente). Después, cualquier triángulo de otra malla (terreno, árboles, muros, otras vías) sobre un tablero con un vértice por encima de su cara inferior se elimina (summary.json rules.bridge_decks) | ground.py (R15), rules.check_bridge_decks | | R16 Guardarraíles reales que paran el coche | Bionda en W con la sección medida del guardarraíl de Kunos (ks_highlands Grail_HI: 0,314 m, borde inferior a 0,44 m), material de Kunos extraído de la instalación del jugador (atlas, normal, maps y detalle de óxido; uso personal) o textura procedural sin AC; postes cada 2 m con separador. La física es una caja invisible cerrada 1WALL_guardrail_col_ de -0,5 a +1,1 m, de una sola cara y normales hacia fuera: la cinta antigua de 33 cm con caras dobles superpuestas no paraba el coche | mesh/guardrail.py, kunos_assets.guardrail_material, barriers.py | | R17 Arcén del material real | El arcén es de lo que es en la realidad: shoulder:surface de OSM (cualquier lado) si está mapeado; si no, asfalto en carreteras clasificadas (autopista a terciaria, enlaces: el arcén español fuera de la línea de borde, malla 1ROAD_shoulder con material y física de asfalto) y cuneta de grava solo en vías locales (1DIRT_shoulder) | RoadSeg.shoulder_paved, RoadNetwork.shoulder_paved_poly, ground.py | | R18 Vegetación apoyada en el suelo | Árboles y arbustos se colocan sobre la malla final del suelo (la que se ve), no sobre el MDT: cerca de las carreteras hay mezclas y taludes, bajo los puentes R15 y entre vértices de las rejillas gruesas el terreno triangulado difiere del MDT varios metros. Cada esquina inferior de cada billboard se pone a la altura del suelo en su posición (-0,25 m), así ninguno queda en el aire en pendiente | ground.GroundSurface, vegetation.build_vegetation(ground_height=) | | R19 Ríos que siguen su valle | Cada cauce se muestrea cada 5 m en el sentido del agua (OSM dibuja aguas abajo; si está dibujado cuesta arriba se invierte). La lámina de agua nunca sube aguas abajo y queda bajo las dos orillas (MDT y luego el suelo final ya mezclado con las carreteras). El terreno se excava: lecho bajo el agua y orillas a 45°, solo bajando puntos de terreno (nunca carreteras). Cauces intermittent/seasonal (barrancos, ramblas), acequias y desagües son lechos secos sin agua. Lagos y balsas planos a la cota de su orilla más baja con fondo excavado. El agua no es física (el coche entra en el cauce), no se dibuja sobre una carretera a nivel (sí bajo puentes) y usa el agua de Kunos. summary.json rules.water cuenta estaciones flotando / enterradas | mesh/rivers.py, ground.py, water.build_water, kunos_assets.water_material | | R20 Línea de IA sobre la física | Cada punto de fast_lane.ai se coloca exactamente sobre la superficie física conducible que tiene debajo. AC (AISpline::calculateNormals) lanza un rayo corto hacia abajo desde cada punto: dos puntos 16 cm bajo el tablero de un puente no lo tocaban y, con el terreno ya bajado bajo el puente (R15), no encontraban nada y AC se cerraba al cargar. También lanza un rayo en el borde izquierdo y derecho de cada punto (sideLeft/sideRight): el ancho de cada lado se ajusta al mayor (en pasos de 0,25 m, mínimo 1 m) con superficie conducible a menos de 0,5 m en los dos lados; en los puentes el borde caía fuera del tablero. Además: el contorno del rectángulo lleva puntos cada 30 m (sin triángulos de kilómetros en los bordes), las celdas pequeñas se unen a su vecina y no se escriben mallas vacías | ailine.snap_heights, pipeline.py, ground.py, lod.chunk_by_grid | | R20c Recorrido sobre sus puentes y túneles | Donde el recorrido va por un tablero o un túnel de sus propias carreteras (tangentes alineadas), su altura es la del tablero: height_at nunca deja que un tramo elevado mande en el suelo, así que en una autopista la línea de IA quedaba 6,8 m por debajo del puente y AC se cerraba al cargar. Pasar por debajo de otro puente no se toca | spawns.course_on_decks | | R20d Bloqueo | Si tras ajustarla queda algún punto de la línea de IA (centro o bordes) sin superficie conducible debajo, la validación da ERROR: un mapa que haría caer a AC nunca se da por bueno | pipeline.py | | R7b Taludes que respetan la ladera | El talud junto a la carretera solo reparte la diferencia entre la carretera y el terreno natural EN SU BORDE (desmonte o terraplén), que se desvanece en 1,8 veces esa diferencia (máx. 150 m); la pendiente natural se conserva. Antes se mezclaba la altura de la carretera con la del propio punto dentro de 90 m: una ladera 600 m por encima de una carretera de montaña se aplanaba y a los 90 m volvía de golpe a su altura, con paredes verdes verticales de cientos de metros | ground.py | | R7c Taludes solo junto a carreteras | El talud solo se aplica junto a superficies cuya altura es la de una carretera (calzada, bordillo, acera, arcén, caminos de tierra y empedrados). Sendas, caminos peatonales y aparcamientos siguen el relieve. Antes contaban como pavimento: el terreno junto a una senda de montaña tomaba la altura de la carretera más cercana, a 1,6 km, y salían paredes de 1.200 m | ground.py | | R21 Horizonte cosido al terreno | El anillo de horizonte se triangula junto con el contorno del rectángulo del terreno detallado (los mismos puntos cada 30 m que pone el suelo en su borde) y toma la altura del suelo ya construido en ellos. Antes no había columnas entre las esquinas del rectángulo: las bandas junto a cada borde eran triángulos de 3,7 km x 90 m con altura solo en las esquinas, que en montaña flotaban como losas sobre el valle, se veían como paredes verdes con la foto estirada y dejaban rendijas transparentes en la unión | horizon.ring_triangulation | | R22 Relieve de 5 m en toda España | El MDT05 del IGN solo se publica en UTM 30 (EPSG:25830), pero cubre toda la península y Baleares: los mapas en las zonas 29 y 31 (Galicia occidental, Cataluña, Aragón oriental, Castellón, Baleares) lo piden en UTM 30 y lo reproyectan a una rejilla de 5 m en su zona. Antes caían al MDT de 30 m de Copernicus: en el Pirineo, laderas facetadas casi verticales con la foto estirada | elevation.IGNProvider | | R23 Incorporaciones y bocas de cruce | OSM dibuja el enlace y la calzada a la que se une como dos vías cuyo asfalto se solapa en la cuña de incorporación; cada una pintaba sus líneas de borde (se cruzaban en X) y ponía su guardarraíl (quedaba en medio del asfalto). Ahora: (a) ninguna línea longitudinal se pinta dentro del asfalto de una vía de mayor rango (clase y luego anchura; un enlace siempre cede a su calzada) ni atravesando una boca de cruce; (b) la línea de borde de la vía principal junto a su enlace se pinta como línea de incorporación de trazo corto (1 m / 1 m, 0,25 m de ancho); (c) los guardarraíles automáticos se deciden punto a punto: nunca donde a 1,2 / 3 / 5 / 8 m hay asfalto de otra vía al mismo nivel o un cruce, y terminan 7 m antes (la nariz de la cuña queda libre); (d) una vía partida en dos en OSM no cuenta como incorporación; un puente o la carretera de debajo (más de 1,5 m de desnivel) tampoco. rules.json → merges cuenta los vértices de guardarraíl sobre asfalto | mesh/merges.py, markings.py, barriers.py | | R24 Incorporaciones y salidas como en la Norma 3.1-IC | OSM une las vías de un enlace en un solo nodo sobre el eje de la calzada que sigue, así que los asfaltos convergían en diagonal y se cruzaban. Ahora, en cada nodo donde se juntan o separan vías rápidas de un sentido: (a) si las que llegan caben en la que sigue (1+1→2 carriles, 2+2→4) cada una va por su lado, pegadas y alineadas los últimos 8 m; (b) si no caben, la más alineada conserva su carril y la otra lleva un carril de cambio de velocidad paralelo de 3,50 m pegado al borde, con la longitud de la Tabla 8.2 (rasante horizontal, velocidades del ramal y del tronco por maxspeed o por clase) y la cuña triangular de la Tabla 8.1 al final (aceleración) o al principio (deceleración), a la altura del tronco; (c) una entrada seguida de una salida del mismo lado dentro de esas longitudes es un único carril de trenzado; (d) las vías sólo se mueven cerca del nodo y se separan donde aún se solapan; el recorrido y la IA siguen el eje de OSM, que queda sobre ese asfalto | mesh/interchange.py | | R25 Cuña entre calzadas | Entre las dos calzadas que se juntan o separan: donde distan menos de 3,5 m, asfalto con cebreado a 45°; más allá, isleta a la altura de los bordes de las dos calzadas (interpolada), berma llana de 2 m y después una caída suave 1:2,5, nunca por debajo del terreno en un terraplén ni por encima de las calzadas en una trinchera. Se calcula sólo entre puntos emparejados de los dos bordes (hasta 30 m de separación) y nunca encima de otra carretera. Antes quedaban fosos de 7 m y triángulos de arcén en punta (‘montañas’) | mesh/gores.py, ground.py, markings.py | | R26 Túneles enterrados y con boca | El túnel era un tubo de hormigón barrido sobre la carretera y nada aseguraba que el terreno lo tapara: con el MDT de 5 m, sobre un falso túnel o un paso inferior el suelo queda por debajo del techo y la caja se veía entera desde fuera (prueba_autopista_freeroad, ronda 37: los dos túneles expuestos, el terreno 2–4 m bajo el techo). Ahora el suelo sobre el tubo está al menos 1,2 m por encima de su techo y cae a 1:1,7 hacia los lados; la cobertura se decide por la posición a lo largo del túnel, así que delante de cada boca no se levanta nada y el acceso queda libre; cada boca lleva una portada de fábrica (dos jambas y un dintel) | mesh/tunnels.py, ground.py, pipeline.build_decks | | R27 Sin cortados artificiales | El suelo que mueven las reglas de carretera (taludes R7, rebaje bajo tableros R15, isletas R25, cobertura R26) no puede quedar más inclinado de 1:1,25 respecto a sus vecinos; el terreno natural no se toca nunca (acantilados y canteras reales se conservan). Además el rebaje bajo un tablero sube ahora 0,6 m por metro en vez de 1,2. Antes quedaban ‘velas’ verticales de tierra junto a cada estribo (370 picos de más de 6 m en prueba_autopista_freeroad) | tunnels.relax_cliffs, ground.py | | R26b Túnel hundido bajo lo que pasa por encima | El perfil de un túnel sale de sus bocas y el MDT, sobre un túnel, da la superficie de arriba: el tubo de 4,5 m atravesaba la carretera que cruzaba por encima, 2 m más baja que su techo. Ahora, igual que un tablero se levanta para dejar gálibo, un túnel bajo una carretera a nivel del suelo se hunde hasta dejar 5,7 m entre calzadas y sus accesos bajan en rampa (<=10 %) | roads._bridge_tunnel_profiles | | R26c Boca abierta | La triangulación del suelo unía la cobertura de detrás de la portada con el terreno de delante y tapiaba la boca. Se recorta un hueco en el terreno en cada boca (0,6 m por fuera, 2,6 m por dentro) y el dintel de la portada se alarga 3,2 m hacia dentro para cubrir el corte | tunnels.mouth_boxes, ground.py | | R28 Pasos inferiores abiertos | R15 solo bajaba el terreno 1 m por debajo del tablero: con un tablero a 6,5 m quedaban 5,5 m de tierra tapando la carretera de abajo (16 cruces bloqueados en prueba_autopista_freeroad). Ahora el corredor de toda carretera que pasa bajo un tablero -y el acceso de 14 m delante de cada boca de túnel- se excava a la altura de esa calzada con 3 m de margen, y sólo sube a 1:1,33 más allá (cara de estribo); nunca se toca el pasillo sobre un tubo. rules.json → tunnels cuenta tubo descubierto, picos y pasos bloqueados | tunnels.carve_underpasses, ground.py | | R29 Cruce a distinto nivel repartido | OSM resuelve muchos cruces marcando sólo la vía de abajo (tunnel=yes, layer=-1) y nada en la de arriba: la V-21 sobre la CV-309 es un viaducto sin etiqueta de puente. Bajar la de abajo el desnivel entero le dejaba una rampa enorme, así que ahora se reparte: la inferior baja como mucho al 5 % (según la longitud real de carretera conectada) y la superior sube el resto al 3 %; la rampa se extiende por la red de carreteras conectadas, no sobre el primer tramo que toca (una caída de 1,5 m sobre un trozo de 6 m era una rampa del 28 %) | roads._spread_height, roads._bridge_tunnel_profiles | | R30 Pendiente máxima por clase | Ninguna vía queda más inclinada de lo que su clase permite (autopista y autovía 7 %, enlaces 8 %, nacional 9 %, secundaria 10 %, comarcal 12 %, resto 16 %): el exceso se reparte por la red con los cruces soldados, y los tableros también ceden (había un puente de autopista al 19,7 %). Sólo se sueldan extremos que se tocan de verdad: unir cualquier par de puntos que coincidan en planta juntaba un puente con la carretera 7 m por debajo y el ajuste divergía. En prueba_autopista_freeroad: 39,3 % → 16,1 %, gálibo mínimo 0,0 → 1,7 m | roads.limit_grades | | R31 La carretera de abajo mantiene su rasante | El MDT es el suelo y el terraplén que sostiene la vía de arriba forma parte de él, así que la carretera inferior tomaba SU altura y subía hacia la estructura (la CV-309 es llana bajo el viaducto de la V-21); luego había que hundirla para recuperar el gálibo: dos cuestas que no existen. Ahora toda la travesía -el trozo que OSM etiqueta como túnel más los metros de acceso que alcanza el relleno- es una recta entre dos puntos de apoyo: el más bajo de los 80 m recorridos a cada lado siguiendo las carreteras conectadas (un relleno sólo sube el terreno, nunca lo baja; el primer punto fuera del relleno seguía estando sobre él). Sólo se nivela donde las vías se cruzan de verdad (tangentes no paralelas), nunca donde discurren en paralelo, y como mucho 200 m. Cuando aun así falta gálibo, sube primero la vía de arriba (la del viaducto) y la de abajo sólo toma lo que quede. prueba_autopista_freeroad: el paso inferior pasa de subir 2,8 m a variar 0,3 m en 95 m | roads.flatten_under_structures, roads._under_chain | | R35 El relleno contamina también a los lados | El terraplén y los estribos que sostienen una vía elevada forman parte del MDT, así que toda carretera cercana tomaba SU altura, no sólo la que pasa justo por debajo: el cruce de la Vía Augusta con la CV-309, junto al estribo de la V-21, quedaba a 12,5 m con sus dos extremos a 8,0 m (ronda 43). La zona contaminada se extiende 25 m a los lados de la vía de arriba y las carreteras dentro de ella se nivelan como en R31. No se tocan las que van paralelas (un vial que sube junto a un terraplén es real) ni las que enlazan con la vía de arriba (su rampa debe subir) | roads.flatten_under_structures | | R36 Dos carreteras sólo se juntan al mismo nivel | La conciliación de cruces emparejaba la carretera de abajo con la V-21 porque en planta pasan a 4 m de su eje, aunque hay 7 m de desnivel, y le copiaba la altura del puente (15,8 m): la nivelación previa se deshacía en la misma generación. Ahora un candidato a cruce con más de 3,5 m de diferencia de altura se descarta: se cruzan, no se unen | roads._reconcile_junctions | | R37 Perfil de corredor en mapas grandes | Por encima de 8 km² el detalle sigue a las carreteras y no al polígono: la ortofoto a máxima resolución sólo se pide en 100 m a cada lado de las vías conducibles (el resto se reescala del mosaico grueso) y el terreno baja de escalón dos veces (fino hasta 40 m del asfalto, medio hasta 100 m, grueso más allá). Antes una isla entera se pedía a 0,25 m/px: son gigabytes de textura que ningún circuito puede cargar y que nadie ve desde la calzada. La web muestra antes de generar el tiempo y el tamaño estimados y avisa del perfil que se va a usar (/api/area/analyze → estimate) | estimate.py, pipeline._ortho_tiles, ground.build_ground, frontend CostBox | | R38 Marcas viales sobre el asfalto y según la norma | La calzada se bombea un 2 % hacia su eje pero la pintura tomaba la altura del eje sin bombeo: en una calzada de 13 m el centro queda 13 cm más alto, así que el 70 % de las líneas centrales y el 64 % de las separaciones de carril quedaban ~2 cm DENTRO del asfalto (medido en dos pistas; los bordes salían bien porque ahí el bombeo es cero). Ahora: (a) una única función roads.crown_rise da el bombeo, y la usan tanto el suelo como la pintura (roads.paint_height), de modo que no pueden discrepar; sobre un tablero la pintura usa la altura del tablero; (b) cada línea se remuestrea cada metro con su propio trazado, así no corta las curvas por dentro ni atraviesa los abombamientos; (c) medidas de la Norma 8.2-IC por velocidad: M-1.1 >100 km/h (3,5/9 m, 15 cm), M-1.2 60–100 (3,5/9, 10 cm), M-1.3 ≤60 (2/5,5, 10 cm), bordes continuos de 15/10 cm; (d) la separación de sentidos es discontinua y sólo se pinta continua donde OSM prohíbe adelantar; (e) los trazos continúan entre los trozos en que OSM parte una vía; (f) nunca se pinta bajo el asfalto de otra calzada que pase por encima. rules.json → markings mide contra la geometría real cuántos puntos quedan enterrados o flotando: 20 % → 1,4 % | roads.crown_rise, markings.py, ground.py | | R39 Un paso a distinto nivel es un paso a distinto nivel, no un cruce en cuesta | Para decidir que una carretera pasa POR DEBAJO de otra se comparaba la cota más alta de una con la más baja de la otra en una banda de 25 m: en un pueblo en ladera eso lo cumple cualquier cruce de calles, así que 128 de las 130 calles de Serra se “nivelaban” contra un punto 80 m cuesta abajo y quedaban hasta 7 m por debajo de su propio suelo (el DEM entre ellas era plano, 0,2–0,8 m). Ahora hacen falta las dos cosas: que los ejes se crucen realmente en planta y que, justo donde se cruzan, la otra calzada esté 1,5 m por encima. Además el gálibo de un puente se reparte como en un paso inferior (R29): la carretera de abajo baja lo que su rampa admite y el tablero sube el resto, porque un MDT de suelo desnudo casi nunca ve la trinchera; antes el tablero se llevaba los 5,2 m enteros y su acceso subía un 10 % flotando sobre las calles vecinas | roads.flatten_under_structures, roads._bridge_tunnel_profiles | | R40 Dos calzadas a distinto nivel nunca comparten suelo | El ajuste que impide que dos calzadas se solapen se hacía antes de calcular las alturas, así que no distinguía dos calles al mismo nivel de dos en terrazas. En un pueblo en ladera dos calles a 4 m en planta y 7 m en altura solapaban su asfalto y el terreno sólo podía triangularlo como un acantilado DENTRO de la carretera: 10.280 de 47.703 muestras por encima del escalón máximo, 8,19 m el peor, 213 picos y 5 puntos de la línea de IA sin superficie (pista INVÁLIDA). Ahora, con las alturas ya definitivas: (a) donde dos calzadas de vías distintas se pisan con desnivel se estrechan —cede primero la de menor categoría, nunca por debajo de 3 m, y la principal sólo da lo que falte conservando el 70 %—; (b) si aun así se tocan, la calle de arriba se aparta lateralmente hacia su propia ladera lo que le falta (mediana 0,72 m, máximo 2,5), sin pasar de las fachadas y sin mover ningún nudo; (c) el desnivel que queda lo sostiene un muro de contención: build_retaining_walls medía la caída contra el MDT, que entre dos calles a 3 m sólo da su media, y ahora la mide contra la carretera de abajo; (d) un paso superior esviado alarga su tablero hasta que el acceso deja de volar sobre la calzada inferior y todos los accesos de ese extremo se recortan al nuevo final del tablero (antes conservaban el nudo viejo, que quedaba bajo el tablero, y su asfalto seguía 6 m por encima de la carretera de abajo). Medido en Serra: acantilados de más de 1 m dentro de la calzada 224 → 108, el peor 16,25 → 2,80 m. rules.json → levels | mesh/levels.py, roads._build_index, roads._extend_decks_over_crossings, barriers.build_retaining_walls | | R41 Un muro sólo donde no cabe un talud, y nunca más alto que uno real | El muro automático medía la caída contra la carretera de abajo sin preguntar cuánto terreno hay entre las dos ni hasta dónde llega: en una herradura de la CV-25, con la tertiary 7 m por encima de la secundaria (desnivel REAL, el propio MDT da 387,4 contra 380,2), salió una cortina de piedra de 8 m pegada a la calzada. Ahora: (a) si el terreno entre las dos tiene 1,5 m de recorrido por cada metro de caída, lo sostiene un talud y no se construye muro; (b) ningún muro pasa de MAX_WALL_H = 4 m: por encima de eso la ladera se abancala, así que el muro se queda con los metros de arriba (los que se ven desde la carretera) y el talud hace el resto | barriers.build_retaining_walls | | R42 Una calzada, un bordillo o una acera es superficie, nunca la cara entre dos niveles | El terreno, la calzada, el bordillo y la acera salen de una misma triangulación, y los triángulos se clasifican por su centro: donde una carretera pasa por encima de otra, las dos aceras se tocan en planta y la malla las unió directamente ladera abajo. Resultado: 854 triángulos de acera colgando como una cortina blanca de 7,3 m sobre la CV-310, que es lo que se veía desde el coche. Ahora un triángulo de superficie que cae más de 1 m con más de 25° de pendiente no es superficie sino el talud entre dos niveles: pasa a terreno (y lo sujeta el muro de R41). Medido en Serra: acera 854 → 86 triángulos (el peor 7,3 → 2,8 m), bordillo 117 → 0, asfalto 54 → 4 | ground.bank_triangles | | R43 El talud entre dos niveles tiene sus propios puntos | La triangulación no tenía nada entre las dos calzadas, así que unía sus bordes directamente: dientes de sierra de triángulos largos que desde el coche se leen como una ladera rota. Ahora se siembra un punto cada 1,5 m a lo ancho de la franja (entre los bordes pavimentados, aceras incluidas), con la altura interpolada entre los dos bordes, y esos puntos no los toca ni el talud de R7 ni la relajación de R27: son la rampa real. Coste medido en Serra (el caso peor, pueblo en ladera + herraduras): +0,33 % de triángulos (10.811 sobre 3,29 M); en la autopista, cero. Y donde el suelo que queda es menos de 0,5 m por metro de caída no hay talud posible: ahí el muro de R41 sube entero (hasta 12 m) en vez de quedarse en 4 | levels.bank_points, ground.build_ground, barriers | | R44 Continua o discontinua según la visibilidad real | Hasta ahora la separación de sentidos sólo salía continua si OSM etiquetaba overtaking=no, que casi nadie pone. La Norma 8.2-IC 3.2.2 dice exactamente cuándo va continua: cuando no hay distancia de visibilidad para completar el adelantamiento. Ahora se calcula esa visibilidad tramo a tramo y por sentido, con observador y obstáculo a 1,2 m del pavimento y a 1 m del borde interior de su carril, y se corta la vista con lo que de verdad la corta: el cambio de rasante (máximo acumulado de pendiente sobre las estaciones de la propia carretera), la ladera (MDT recortado por el desmonte que abren las reglas de R7) y los edificios. Tabla 1 de la norma (DVN: 50 m a 40 km/h … 250 m a 100 km/h) para empezar y terminar la continua; dos prohibiciones separadas por menos de esa distancia se unen (no da tiempo a adelantar entre ellas); antes de cada continua va la zona de preaviso de la tabla 5 (95–215 m) pintada con M-1.9/M-1.10, cuyo trazo es más largo que el vano (10 trazos = 48,5 m con VM>60 y 29 m con VM≤60, que es como la propia norma los define). Si sólo un sentido se queda sin visibilidad se pinta M-3.2: continua en el eje y discontinua adosada del lado que sí puede adelantar. La VM es la que diga OSM y, si no dice nada, la de la clase limitada por lo que permiten sus curvas (radio mediano, V=√(27,9·R)): sin eso una carretera de montaña sin etiqueta se tomaba como de 90 y salía continua casi entera. Medido: CV-310 48 % continua, autopista 0,9 %. rules.json → overtaking | mesh/sight.py, markings.py | | R45 Marcas transversales, ceda el paso y flechas de retorno | Segunda tanda de la Norma 8.2-IC. (a) La continua obligatoria que no depende de la visibilidad: todo acceso de una vía a un nudo con otra de mayor categoría (longitud mínima de la norma: 29 m con VM≤60, 48,5 m con VM>60), los metros antes de un paso a nivel y el sentido que converge donde la calzada se estrecha por debajo de 3,25 m por carril. (b) M-4.2: el ceda el paso español es una línea transversal DISCONTINUA de 0,4 m de ancho con trazos de 0,8 y vanos de 0,4 —no los dientes de tiburón de otros países, que es lo que pintábamos—, y el trazo junto al eje sale entero, como pide 3.4.2.1; la línea de detención M-4.1 es continua de 0,40 m para stop, ceda y semáforo. (c) M-4.3: el paso de peatones pasa a 4 m de fondo (2,5 sólo por debajo de 40 km/h), bandas de 0,5 con vanos de 0,5, el eje de la calzada cae en el centro de una banda y no se pinta ninguno en vías de más de 60 km/h. (d) M-5.5 flechas de retorno en los vanos de la zona de preaviso: la primera en el centro del segundo vano antes de la continua, la siguiente dejando uno libre y las demás cada vez más (2, 4 o 5 según VM), sólo para prohibiciones de más de 40 m. Medido en Serra: 168 flechas, 61 % del eje cerrado; en la autopista el 5,4 % (los accesos a las vías prioritarias) | markings.py, sight.mandatory_solid | | R46 Inscripciones con la forma que dibuja la norma | Las cotas del STOP y del ceda el paso no están en el texto del BOE sino en sus figuras, así que se leyeron del propio documento en vez de inventarlas: las letras del M-6.3/M-6.4 son los trazados vectoriales de la figura (la palabra sale 0,533 veces el alto de la letra, es decir 2,13 m para letras de 4 m, contra los 2,12 m que la figura acota: 0,5 % de error), y el triángulo M-6.5 se construye con las cotas de su figura (3,6 m de largo, 1,2 m de base, trazo 0,18 y banda maciza de 0,6). Letra de 4 m con VM>60 y de 1,6 m con VM≤60 (3.6); la inscripción va 12 m antes de la línea —la norma permite de 2,5 a 25— para quedar detrás del paso de peatones, que es el orden con que se las encuentra el conductor. Las letras se trianguian con huecos (la O y la P conservan su contorno interior). Además la flecha de retorno engorda a 0,15 de trazo y 0,60 de punta, que a la distancia a la que se conduce se | R47 Cada modelo de la biblioteca es un objeto real con su textura horneada | Los 159 modelos eran maquetas de cajas con colores planos (218 triángulos de media, texturas de 16 px), el coche aparcado eran tres cajas, la señal un cuadrado plano y el buzón, el hidrante o la grúa un cilindro hecho en código. Ahora son 258 modelos (los 159 rehechos y 99 nuevos: 8 coches, 70 señales, mobiliario, hitos) hechos con scripts/assetkit (Blender headless): geometría de silueta y texturas propias horneadas —color con la oclusión ambiental y el desgaste calculado sobre el propio objeto, mapa de normales, brillo—; donde Poly Haven (CC0) tiene el objeto, su escaneado es la fuente. El motor empaqueta en atlas de 2048 px sólo los modelos que usa cada pista (mesh/atlas.py) y pinta cada coche aparcado de su color con la máscara de pintura. Un modelo cuyo pie no cabe junto a la calzada no se coloca (antes R2 le recortaba los triángulos y dejaba objetos agujereados); los que van sobre ella a propósito —pórticos, peaje, barrera— quedan enteros. Métrica: rules.carriageway.removed_triangles (puerto: 10 753 → 660). | | R48 Cada señal es la que dice el mapa, y mira a quien debe leerla | mesh/signkinds.py traduce el código de la norma 8.1-IC que anota OpenStreetMap (ES:R305) y la clase que Mapillary reconoce en las fotos (warning--curve-right) al modelo sign_<tipo>, y orienta la señal con la dirección que da Mapillary (coincide con la regla del lado de la calzada en el 74 % de los casos y corrige el 22 % en que la regla falla). Una señal que nadie pudo leer nunca da una orden al conductor: es una placa neutra (texto, aparcamiento, carga y descarga, dirección), y los paneles de destino ya no se confunden con flechas de sentido obligatorio. | lee como flecha y no como un siete. La inscripción se sitúa caminando a lo largo de la carretera, no sobre la tangente: en una curva, 12 m en línea recta la sacaban del asfalto y la palabra acababa pintada sobre el arcén (el 15 % de la pintura de nudo estaba fuera de la calzada; ahora lo que queda son los 6 cm del final de la línea de detención muriendo contra el bordillo, que es lo que hace una de verdad) | | R11 Tramos de tierra | Un camino sin asfaltar (surface de tierra/grava o highway=track sin pavimento) no lleva arcén de grava ni cuneta: su borde se funde con el terreno; su malla es 1LOOSE_unpaved con superficie LOOSE (válida para cronometrar, fricción 0,80 como las zonas de grava de Laguna Seca/Zandvoort, vibración ligera y SIN oscilación SIN_HEIGHT, que hacía botar el coche); textura: la foto de camino de tierra de Kunos Zandvoort (Starada_Sterrata_002, uso personal) con repetición de 6 m, o la CC0 si AC no está instalado. DIRT queda para el terreno fuera de la pista | RoadSeg.shoulder, ground.py, inifiles.SURFACES, kunos_assets.dirt_road_texture | | R7 Taludes creíbles | Desmontes y terraplenes junto a la carretera nunca superan ~30° (anchura del talud ≥ 1,8·desnivel); la ortofoto (el paisaje real) se mantiene en toda la ladera y solo se suaviza un 50 % en caras >45° (la malla de roca por triángulo se descartó: leía como pinchos en la rejilla de 12 m) | ground.py (mezcla, _split_cliffs), ortho_tiles.build_masks(roads=) |

Interfaz web (http://127.0.0.1:5173): el primer paso es el TIPO de mapa (Free roam / Rally stage / Circuit / Designed); free roam y circuito se seleccionan por área; el tramo se selecciona pinchando las carreteras en el mapa en orden de marcha (GET /api/roads/tile?tx&ty sirve teselas fijas de 0,05° cacheadas en disco para siempre; el servidor prueba primero el espejo de Overpass que respondió la última vez y, si falla, lanza la consulta a todos a la vez; el navegador pide solo las teselas que faltan, 4 a la vez, las más cercanas primero, y las dibuja en canvas según llegan; con extractos regionales de Geofabrik activados ([providers] osm_extracts = true, por defecto) la primera vez que se usa el selector en una región se descarga su extracto .osm.pbf (Valencia: 140 MB, una sola vez) y se cortan sus vías circulables en las mismas teselas (cache/region_tiles/<región>/<tx>_<ty>.jsonl); desde entonces las teselas de esa región salen del disco al instante (GET /api/roads/region informa del progreso y el selector lo muestra), y mientras tanto, o en teselas que cruzan el límite de la región, se usa Overpass; GET /api/roads queda para rectángulos libres, y ambos cargan solo las vías circulables, incluidos caminos de tierra highway=track y vías de servicio; los caminos se construyen como calzada sin asfaltar con física DIRT) y el área se calcula alrededor de ellas (margen configurable); el backend recibe route_way_ids y las trata como la carretera prioritaria del tramo. El ORDEN de los clics manda (route.ordered_stage_route): la carretera 1 se recorre solo hasta el cruce con la carretera 2 (salida elegida, tolerancia 4 m para cruces en T sin nodo común), la 2 hasta el cruce con la 3… y la última hasta su extremo; el resto de una carretera más allá de la salida elegida se cierra con barrera como cualquier otra salida (la exención de cierre es geométrica: solo los trozos que coinciden con el trazado). Si dos carreteras consecutivas no se tocan (a más de 4 m), se unen por el camino circulable más corto de toda la red (hasta 400 m) insertando esos tramos de conexión: OSM corta las carreteras en trocitos en cada cruce y es fácil no pinchar uno de 10 m; solo si no hay conexión en ese radio se usa el camino más largo por la selección. El punto donde se pincha cada carretera decide el lado: de la carretera 1 se conserva la parte pinchada (desde su extremo hasta la salida) y de la última la dirección pinchada desde su cruce; el selector dibuja en vivo el tramo recortado (frontend/src/stageCourse.ts, cruce = máxima aproximación ≤ 8 m) con salida verde y meta roja, difumina lo que no se recorre, avisa si dos carreteras no se tocan y calcula el área alrededor del tramo recortado; los puntos de clic viajan como route_anchors y el backend (ordered_stage_route(anchors=)) genera exactamente ese recorrido. Al pulsar Generar aparece un aviso fijo (arriba a la derecha, en cualquier paso o pestaña) con etapa y porcentaje, el título de la pestaña muestra el progreso y el navegador lanza una notificación de escritorio al empezar y al terminar (si se concede el permiso).

Modo tramo (--stage --road REF): la carretera elegida es prioritaria (muestreo a 1,5 m, radio vertical mínimo 450 m), se cierran las salidas laterales con barreras “carretera cortada” solo cuando la barrera queda fuera de la calzada del tramo (se prueba a 6/9/13/18/24 m dentro de la vía lateral; nunca sobre un tramo de la propia carretera elegida, esté o no en la ruta), flechas de rally y pacas en las curvas fuera de zonas edificadas (≥3 edificios a 45 m = casco urbano, sin atrezzo), cinta de espectadores y comisarios, pórticos de celosía de salida y meta con pancarta impresa (stage_start/stage_finish, texturas banner_start/banner_finish), carpa de asistencia; sin coches aparcados. Cronometraje con la disposición de trento-bondone (subida A2B que cronometra bien en AC): AC_AB_START en el pórtico de SALIDA (arranca el tiempo), AC_AB_FINISH en el pórtico de META (lo detiene), AC_TIME_0 solo como parcial a mitad del tramo, sin AC_TIME_1/2 ni AC_OPEN_FINISH (meta de drag), y etiqueta A2B en ui_track.json. Con AC_TIME_0 sobre la salida AC contaba una vuelta de circuito que nunca se cerraba; el validador rechaza ahora esa disposición.

4. Bucle de validación

  • worldlaps validate: nombres físicos, superficies, spawn, IA, escala, nodos.
  • Renders Blender (docs/renders/*): comprobación geométrica rápida.
  • En el juego: FPS, aspecto de cerca, noche. El log de CSP (Documentos/Assetto Corsa/logs/custom_shaders_patch.log) dice qué secciones del ext_config.ini se aplicaron (ejemplo real: los comodines deben ser ?, no *).
  • Feedback numerado del usuario → cada punto se convierte en una fila de docs/REALISM_OPTIONS.md con estado.

5. Estado y siguientes pasos (13-09-2026)

Hecho: todo lo de la tabla del punto 2 salvo: gramática de fachadas ampliada (en curso), rotondas dedicadas (en curso), vallas de parcelas industriales (en curso), estructuras altas como cilindros (en curso). Siguiente: fachadas 3D (balcones/aleros) cerca de la ruta, alturas reales fuera de España (EUBUCCO/Overture), PNOA-LiDAR para cubiertas exactas, texturas de fachada por tipología fotografiadas (CC0) por región.