#define pi acos(-1.)
const float tau=6.283185;
#define rot(spin)mat2(cos(spin),sin(spin),-sin(spin),cos(spin))


// 以下の値を変えるとコースやレースが変わります
// Changing the following values will change the course and race.
const float COURCEID = 217.;

const bool ONGRID = false;
const bool ONRACEORDER = false;
const bool ONDRAWCENTER=false;

const float onBoardCameraProb=.3;
const bool onBoardCamera=false;
const bool backCamera=false;

const int RACERNUM = 8;

//LAPDATA
const float ALLLAP = 50.;
const float BEFORELAP = 5.;
const float STARTLAP = 1.;
const float AFTERLAP = 5.;
const float ENDLAP = 5.;
const float RACELAP = ALLLAP - BEFORELAP - AFTERLAP - ENDLAP;

// スタート時の逆流を防ぐ応急処理
float extraMileage = 1.75;

float progress;
float beforeProgress;
float startProgress;
float finishProgress;

const float marbleSize = .008;
const float roadWidth = .1;

const float cityHeight = 0.05;
const float cityScale = 32.;

#define STARTPOSITION vec2(0.,0.)
#define INITIALPOS vec2(0.,.5)+STARTPOSITION
#define INITIALDIR vec2(1.,0.)

const float fogDensity=1.5;
const float fogDistance=0.1;
const vec3 fogColor=vec3(.34,.37,.4);

const float windowSize=.3/cityScale;
const float windowDivergence=.2/cityScale;
const vec3 windowColor=vec3(.1,.2,.5);

const float streetDistance=.015;
const vec3 streetColor=vec3(4.,8.,10.);

const float beaconProb=.003;
const float beaconFreq=.6;
const vec3 beaconColor=vec3(1.5,.2,0.);
const vec3 beaconGreenColor=vec3(.2,1.5,0.);

const vec3 roadGlow=vec3(1,.3,.2)*2E-5;

vec4 RACERNUMCOLOR[8]=vec4[](
    vec4(1.,0.,0.,1.),// Red
    vec4(0.,1.,0.,1.),// Green
    vec4(0.,0.,1.,1.),// Blue
    vec4(1.,1.,0.,1.),// Yellow
    vec4(1.,0.,1.,1.),// Magenta
    vec4(0.,1.,1.,1.),// Cyan
    vec4(.5,.5,.5,1.),// Gray
    vec4(1.,1.,1.,1.)// White
);

//   _ __   ___ (_)___  ___ 
//  | '_ \ / _ \| / __|/ _ \
//  | | | | (_) | \__ \  __/
//  |_| |_|\___/|_|___/\___|

float hash1(vec2 p2){
    p2=fract(p2*vec2(5.3983,5.4427));
    p2+=dot(p2.yx,p2.xy+vec2(21.5351,14.3137));
    return fract(p2.x*p2.y*95.4337);
}

float hash1(vec2 p2,float p){
    vec3 p3=fract(vec3(5.3983*p2.x,5.4427*p2.y,6.9371*p));
    p3+=dot(p3,p3.yzx+19.19);
    return fract((p3.x+p3.y)*p3.z);
}

vec2 hash2(vec2 p2){
    vec3 p3=fract(vec3(5.3983*p2.x,5.4427*p2.y,6.9371*p2.x));
    p3+=dot(p3,p3.yzx+19.19);
    return fract((p3.xx+p3.yz)*p3.zy);
}

vec2 hash2(vec2 p2,float p){
    vec3 p3=fract(vec3(5.3983*p2.x,5.4427*p2.y,6.9371*p));
    p3+=dot(p3,p3.yzx+19.19);
    return fract((p3.xx+p3.yz)*p3.zy);
}

vec3 hash3(vec2 p2){
    vec3 p3=fract(vec3(p2.xyx)*vec3(5.3983,5.4427,6.9371));
    p3+=dot(p3,p3.yxz+19.19);
    return fract((p3.xxy+p3.yzz)*p3.zyx);
}

float noise1(vec2 p){
    vec2 i=floor(p);
    vec2 f=fract(p);
    vec2 u=f*f*(3.-2.*f);
    return mix(mix(hash1(i+vec2(0.,0.)),
    hash1(i+vec2(1.,0.)),u.x),
    mix(hash1(i+vec2(0.,1.)),
    hash1(i+vec2(1.,1.)),u.x),u.y);
}

float Hash21(vec2 p){
    p=fract(p*vec2(234.24,234.535));
    p+=dot(p,p+34.23+COURCEID);
    return fract(p.x*p.y);
}

// Simplex 2D noise
vec3 permute(vec3 x){return mod(((x*34.)+1.)*x,289.);}

float snoise(vec2 v){
    const vec4 C=vec4(.211324865405187,.366025403784439,
    -.577350269189626,.024390243902439);
    vec2 i=floor(v+dot(v,C.yy));
    vec2 x0=v-i+dot(i,C.xx);
    vec2 i1;
    i1=(x0.x>x0.y)?vec2(1.,0.):vec2(0.,1.);
    vec4 x12=x0.xyxy+C.xxzz;
    x12.xy-=i1;
    i=mod(i,289.);
    vec3 p=permute(permute(i.y+vec3(0.,i1.y,1.))
    +i.x+vec3(0.,i1.x,1.));
    vec3 m=max(.5-vec3(dot(x0,x0),dot(x12.xy,x12.xy),
    dot(x12.zw,x12.zw)),0.);
    m=m*m;
    m=m*m;
    vec3 x=2.*fract(p*C.www)-1.;
    vec3 h=abs(x)-.5;
    vec3 ox=floor(x+.5);
    vec3 a0=x-ox;
    m*=1.79284291400159-.85373472095314*(a0*a0+h*h);
    vec3 g;
    g.x=a0.x*x0.x+h.x*x0.y;
    g.yz=a0.yz*x12.xz+h.yz*x12.yw;
    return 130.*dot(m,g);
}

//   _____           _   
//  |  ___|__  _ __ | |_ 
//  | |_ / _ \| '_ \| __|
//  |  _| (_) | | | | |_ 
//  |_|  \___/|_| |_|\__|
// kinankomoti font_logging https://www.shadertoy.com/view/mdVSWz
#define FontWidth 8
#define FontHeight 8
#define LineMaxLength 40

ivec2 font_data[]=ivec2[](
    //0
    ivec2(0x00000000,0x00000000),//space
    
    //1~10
    ivec2(0x7e91897e,0x00000000),//0
    ivec2(0x01ff4121,0x00000000),//1
    ivec2(0x71898543,0x00000000),//2
    ivec2(0x6e919142,0x00000000),//3
    ivec2(0x08ff4838,0x00000000),//4
    ivec2(0x8e9191f2,0x00000000),//5
    ivec2(0x0e91916e,0x00000000),//6
    ivec2(0xc0b08f80,0x00000000),//7
    ivec2(0x6e91916e,0x00000000),//8
    ivec2(0x6e919162,0x00000000),//9
    
    //11~36
    ivec2(0x1e11110e,0x00000001),//a
    ivec2(0x0e11117f,0x00000000),//b
    ivec2(0x0a11110e,0x00000000),//c
    ivec2(0x7f11110e,0x00000000),//d
    ivec2(0x0815150e,0x00000000),//e
    ivec2(0x48483f08,0x00000000),//f
    ivec2(0x3e494930,0x00000000),//g
    ivec2(0x0708087f,0x00000000),//h
    ivec2(0x012f0900,0x00000000),//i
    ivec2(0x5e111102,0x00000000),//j
    ivec2(0x000b047f,0x00000000),//k
    ivec2(0x017f4100,0x00000000),//l
    ivec2(0x0807080f,0x00000007),//m
    ivec2(0x0708080f,0x00000000),//n
    ivec2(0x06090906,0x00000000),//o
    ivec2(0x1824243f,0x00000000),//p
    ivec2(0x3f242418,0x00000000),//q
    ivec2(0x0010081f,0x00000000),//r
    ivec2(0x0012150d,0x00000000),//s
    ivec2(0x11113e10,0x00000000),//t
    ivec2(0x0f01010e,0x00000000),//u
    ivec2(0x000e010e,0x00000000),//v
    ivec2(0x010e010e,0x0000000f),//w
    ivec2(0x0a040a11,0x00000011),//x
    ivec2(0x3e090930,0x00000000),//y
    ivec2(0x00191513,0x00000000),//z
    
    //36~63
    ivec2(0x7f88887f,0x00000000),//A
    ivec2(0x6e9191ff,0x00000000),//B
    ivec2(0x4281817e,0x00000000),//C
    ivec2(0x7e8181ff,0x00000000),//D
    ivec2(0x919191ff,0x00000000),//E
    ivec2(0x909090ff,0x00000000),//F
    ivec2(0x4685817e,0x00000000),//G
    ivec2(0xff1010ff,0x00000000),//H
    ivec2(0x0081ff81,0x00000000),//I
    ivec2(0x80fe8182,0x00000000),//J
    ivec2(0x413608ff,0x00000000),//K
    ivec2(0x010101ff,0x00000000),//L
    ivec2(0x601060ff,0x000000ff),//M
    ivec2(0x0c1060ff,0x000000ff),//N
    ivec2(0x7e81817e,0x00000000),//O
    ivec2(0x609090ff,0x00000000),//P
    ivec2(0x7f83817e,0x00000001),//Q
    ivec2(0x619698ff,0x00000000),//R
    ivec2(0x4e919162,0x00000000),//S
    ivec2(0x80ff8080,0x00000080),//T
    ivec2(0xfe0101fe,0x00000000),//U
    ivec2(0x0e010ef0,0x000000f0),//V
    ivec2(0x031c03fc,0x000000fc),//W
    ivec2(0x340834c3,0x000000c3),//X
    ivec2(0x300f30c0,0x000000c0),//Y
    ivec2(0xe1918d83,0x00000081),//Z
    
    //63~
    ivec2(0x00007d00,0x00000000),//!
    ivec2(0x60006000,0x00000000),//"
    ivec2(0x3f123f12,0x00000012),//#
    ivec2(0x52ff5224,0x0000000c),//$
    ivec2(0x33086661,0x00000043),//%
    ivec2(0x374d5926,0x00000001),//&
    ivec2(0x00006000,0x00000000),//'
    ivec2(0x0081423c,0x00000000),//(
    ivec2(0x003c4281,0x00000000),//)
    ivec2(0x00143814,0x00000000),//*
    ivec2(0x00103810,0x00000000),//+
    ivec2(0x00020100,0x00000000),//,
    ivec2(0x08080808,0x00000000),//-
    ivec2(0x00000100,0x00000000),//.
    ivec2(0x30080601,0x00000040),///
    ivec2(0x00240000,0x00000000),//:
    ivec2(0x00240200,0x00000000),//;
    ivec2(0x41221408,0x00000000),//<
    ivec2(0x00141414,0x00000000),//=
    ivec2(0x08142241,0x00000000),//>
    ivec2(0xa999423c,0x0000007c),//@
    ivec2(0x008181ff,0x00000000),//[
    ivec2(0x06083040,0x00000001),//\//
    ivec2(0x00000000,0x00000000),//] 何故か表示されない
    ivec2(0x00ff8181,0x00000000),//]
    ivec2(0x20402010,0x00000010),//^
    ivec2(0x01010101,0x00000000),//_
    ivec2(0x40408080,0x00000000),//`
    ivec2(0x41413608,0x00000000),//{
    ivec2(0x00ff0000,0x00000000),//|
    ivec2(0x08364141,0x00000000),//}
    ivec2(0x08101008,0x00000010)//~
    
);

vec3 font(vec2 uv,int id){
    vec2 uv1=uv;
    uv=uv*8.;
    ivec2 texel=ivec2(uv);
    int bit_offset=texel.x*FontWidth+texel.y;
    
    int s,t;
    s=font_data[id].x;
    t=font_data[id].y;
    
    int tex=0;
    
    if(bit_offset<=31){
        s=s>>bit_offset;
        s=s&0x00000001;
        tex=s;
    }
    else{
        t=t>>(bit_offset-32);
        t=t&0x00000001;
        tex=t;
    }
    
    tex=(abs(uv1.x-.5)<.5&&abs(uv1.y-.5)<.5)?tex:0;
    return vec3(tex);
}


//           _  __ 
//   ___  __| |/ _|
//  / __|/ _` | |_ 
//  \__ \ (_| |  _|
//  |___/\__,_|_|  
bool drawCircle(vec2 uv,vec2 pos){
    float posPoint=.075-length(pos-uv);
    return ceil(posPoint)==1.;
}

float calculateGridLine(vec2 uv,float grid_size,float line_width){
    
    // グリッドの線を計算
    float line_x=step(grid_size-line_width,mod(uv.x-line_width/2.,grid_size));
    float line_y=step(grid_size-line_width,mod(uv.y-line_width/2.,grid_size));
    
    float line=line_x+line_y;
    
    return line;
}

float sdSphere(vec3 p,float r)
{
    return length(p)-r;
}

//    ____ _____ _     _     
//   / ___| ____| |   | |    
//  | |   |  _| | |   | |    
//  | |___| |___| |___| |___ 
//   \____|_____|_____|_____|
int getcell(vec2 pos){
    return int(floor(Hash21(mod(pos,vec2(64.)))*3.));
}

float passedCellNum(float mileage){
    return floor(mileage);
}

struct TruchetData{
    vec2 pos;
    vec2 dir;
    vec2 floorpos;
};

TruchetData allSkippedData[128];
int lastSkippedDataIndex;

#define EXTRACELL 5.
void setSkipTruchetData(vec2 pos,vec2 dir, vec2 floorpos, float mileage){
    for(float i=0.;i<passedCellNum(RACELAP) + EXTRACELL;i++){
        // for(float i=0.;i<floor(fract(time*.01)*100.);i++){
        pos+=dir*.5;// centerPosがタイルの中央に乗る
        int celltype=getcell(floorpos);//現在の位置のセルタイプを取得
        
        // セルタイプが0の場合、左に曲がるよう方向を転換
        if(celltype==0){
            dir=dir.yx;
        // セルタイプが1の場合、右に曲がるよう方向を転換
        }else if(celltype==1){
            dir=-dir.yx;
        }
        // セルタイプが2の場合、方向を変えずに進むため方向転換無し
        
        floorpos+=dir;// 現在のタイル位置を次のタイル位置に更新
        pos+=dir*.5;// centerPosが次のタイルの端に乗る

        allSkippedData[int(i)]=TruchetData(pos,dir,floorpos);
    }
}

vec2 getCachedSkipTruchetPos(inout vec2 pos,inout vec2 dir,inout vec2 floorpos, float mileage){
    int index = int(passedCellNum(mileage));
    TruchetData data=allSkippedData[index];
    pos=data.pos;
    dir=data.dir;
    floorpos=data.floorpos;
    return pos;
}

vec4 getNowOnTruchetPos(inout vec2 pos,inout vec2 dir,inout vec2 floorpos,float mileage,float sidePosition){
    float multiplier=1.;

    int celltype=getcell(floorpos);
    vec2 nextDir;
    if(celltype==0){
        nextDir=dir.yx;
    }else if(celltype==1){
        nextDir=-dir.yx;
    }

    float corner=dir.x*nextDir.y-dir.y*nextDir.x;

    // 仕組みがわからないですが観察結果から、横移動の場合は左が逆転する
    float side = 1.;
    if(dir.x!=0.) side = -1.;


    // affectが正なら右に移動
    float affect = sidePosition*(roadWidth*5.);
    //　左回転
    if(0.<corner){
        pos+=dir.yx * affect * .5 * side;
        multiplier=1.+affect;
    }
    //　右回転
    if(corner<0.){
        pos+=dir.yx * affect * .5 * side;
        multiplier=1.-affect;
    }
    // 直進
    if(corner==0.){
        //dirのy,xを入れ替えたものに、xに-1をかけたものが左になる
        pos+=dir.yx * vec2(1,-1) * affect * .5;
    }

    

    // mileageに応じて位置調整
    pos+=dir*sin(fract(mileage)*pi*.5)*.5 * multiplier;

    vec2 dirForRacingObject=dir;
    if(celltype==0){
        dirForRacingObject*=rot(dot(abs(dir),vec2(-1.,1.))*fract(mileage)*pi*.5);
        dir=dir.yx;
    }else if(celltype==1){
        dirForRacingObject*=rot(dot(abs(dir),vec2(1.,-1.))*fract(mileage)*pi*.5);
        dir=-dir.yx;
    }
    pos+=dir*(.5-cos(fract(mileage)*pi*.5)*.5) * multiplier;
    return vec4(pos,dirForRacingObject);
}

//   ____                     
//  |  _ \ __ _  ___ ___ _ __ 
//  | |_) / _` |/ __/ _ \ '__|
//  |  _ < (_| | (_|  __/ |   
//  |_| \_\__,_|\___\___|_|   
struct Racer{
    vec2 pos;
    vec2 dirForRacingObject;
    vec4 color;
    float ID;
    float mileage;
};

Racer createRacerFromTruchet(float mileage,float sidePosition, vec4 color, float ID){
    vec2 racerPos;
    vec2 dir;
    vec2 floorpos;
    racerPos=getCachedSkipTruchetPos(racerPos,dir,floorpos,mileage);
    vec4 getNowOnTruchetPosResult = getNowOnTruchetPos(racerPos,dir,floorpos,mileage,sidePosition);
    return Racer(getNowOnTruchetPosResult.xy,getNowOnTruchetPosResult.zw,color,ID, mileage);
}


//   ____             _
//  / ___|  ___  _ __| |_
//  \___ \ / _ \| '__| __|
//   ___) | (_) | |  | |_
//  |____/ \___/|_|   \__|
// バブルソートで任意の数の構造体配列をソートする関数
void bubbleSort(inout Racer arr[RACERNUM],int size){
    for(int i=0;i<size-1;i++){
        for(int j=0;j<size-i-1;j++){
            if(arr[j].mileage<arr[j+1].mileage){
                // 値を入れ替える
                Racer temp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
            }
        }
    }
}

//   ____                      
//  / ___|  ___ ___ _ __   ___ 
//  \___ \ / __/ _ \ '_ \ / _ \
//   ___) | (_|  __/ | | |  __/
//  |____/ \___\___|_| |_|\___|
float getRoadOrCity(vec2 block, float roadWidth){
    // ■ タイルごとのUV生成
    vec2 flooruv=floor(block);
    vec2 uvOfTile=block-flooruv-.5;
    int celltype=getcell(flooruv.xy);

    // ■ 道の描画
    //uvから道のもとになるlenを作る
    vec2 uvOfTileForRoad=uvOfTile;
    if(celltype==0)uvOfTileForRoad*=vec2(1.,-1.);
    uvOfTileForRoad*=step(0.,uvOfTileForRoad.x+uvOfTileForRoad.y)*2.-1.;
    float len;
    if(celltype==2){
        len=min(abs(uvOfTileForRoad.x),abs(uvOfTileForRoad.y));
    }else{
        len=length(uvOfTileForRoad-.5)-.5;
    }

    //道を描画
    float roadSize=(abs(len)-roadWidth)*iResolution.y*.1;
    float clampedRoad=clamp(roadSize,0.,1.);

    return clampedRoad;
}

// inspired by Glow City mhnewman https://www.shadertoy.com/view/XlsyWB
#define NOHIT 0.
#define HIT_CITY 1.
#define HIT_ROAD 2.

struct CityResult{
    float distance;
    float beacon;
    float face;
    float hittedTarget;
    float distanceFromEye;
    vec3 hitPos;
};

CityResult cityRay(vec3 eye,vec3 ray){
    eye.xyz = eye.xzy;
    ray.xyz = ray.xzy;
    vec2 block=floor(eye.xy*cityScale)/cityScale;
    vec3 ri=1./ray;
    vec3 rs=sign(ray);
    vec3 side=.5+.5*rs;
    vec2 ris=ri.xy*rs.xy;
    vec2 dis=(block-eye.xy+.5/cityScale+rs.xy*.5/cityScale)*ri.xy;
    
    float hit=0.;
    float beacon=0.;

    vec3 resultPos;

    float dist=500.;
    float face=0.;
    
    for(int i=0;i<150;++i){
        // 高さの計算を元に戻し、建物ごとにランダム化
        // 面の交差判定を安定させるために、1/3スケールに調整
        vec2 lo0=vec2(block+.01/cityScale);// 面の下端を調整
        vec2 loX=vec2(.3,.3)/cityScale;
        vec2 hi0=vec2(block+.69/cityScale);// 面の上端を調整
        vec2 hiX=vec2(.3,.3)/cityScale;
        float height=(.5+hash1(block))*(2.+4.*pow(noise1(.1*block),2.5))*cityHeight;
        float clampedRoad=getRoadOrCity(block,roadWidth);
        height *= clampedRoad;
        
        dist=500.;
        face=0.;
        
        // ビルの各面について交差判定を行う
        for(int j=0;j<int(float(3)*clampedRoad);++j){
        // for(int j=0;j<1;++j){
            float top=height*(1.-.1*float(j));
            vec3 lo=vec3(lo0+loX*hash2(block,float(j)),0.);
            vec3 hi=vec3(hi0+hiX*hash2(block,float(j)+.5),top);
            
            vec3 wall=mix(hi,lo,side);// 交差面の位置
            vec3 t=(wall-eye)*ri;
            
            vec3 dim=step(t.zxy,t)*step(t.yzx,t);
            float maxT=dot(dim,t);
            float maxFace=1.-dim.z;
            
            resultPos=eye+maxT*ray;
            if(resultPos.z<0.0){
                return CityResult(dist,beacon,face,HIT_ROAD,length(resultPos-eye),resultPos.xzy);
            }
            dim+=step(lo,resultPos)*step(resultPos,hi);// 面内判定
            if(dim.x*dim.y*dim.z>.5&&maxT<dist){
                dist=maxT;
                face=maxFace;
            }
        }
            
        vec2 h=hash2(block);
        if(h.x<beaconProb){
            vec3 center=vec3(block+.5/cityScale,height);
            float t=dot(center-eye,ray);
            if(t<dist){
                vec3 p=eye+t*ray;
                float fog=(exp(-p.z/fogDistance)-exp(-eye.z/fogDistance))/ray.z;
                fog=exp(fogDensity*fog);
                
                t=distance(center,p);
                fog*=smoothstep(1.,.5,cos(tau*(beaconFreq*iTime+h.y)));
                beacon+=fog*pow(clamp(1.-t*25.0,0.,1.),4.);
            }
        }

        // スタートシグナル
        if(0. < beforeProgress && startProgress < 1.){
            int index=int(passedCellNum(0.));
            TruchetData data=allSkippedData[index];
            vec2 startLine = floor(data.pos*cityScale)/cityScale;
            
            if(startLine.x - roadWidth + 1./cityScale < block.x
             && block.x < startLine.x + roadWidth * (beforeProgress+0.2) - 1./cityScale
             && startLine.y + 0.25 < block.y
             && block.y< startLine.y+.25+2./cityScale
             ){
                vec3 center=vec3(block+.5/cityScale,.03);
                float t=dot(center-eye,ray);
                if(t<dist){
                    vec3 p=eye+t*ray;
                    float fog=(exp(-p.z/fogDistance)-exp(-eye.z/fogDistance))/ray.z;
                    fog=exp(fogDensity*fog);
                    t=distance(center,p);
                    beacon+=fog*pow(clamp(1.-t*25.,0.,1.),4.)*10.;
                }
            }
        }

        // ゴールポイント
        int index=int(passedCellNum(RACELAP + extraMileage));
        TruchetData data=allSkippedData[int(index)];
        vec2 startLine=floor(data.pos*cityScale)/cityScale;

        if(startLine.x-roadWidth<block.x
            &&block.x<startLine.x+roadWidth
            &&block.y<startLine.y+roadWidth
            &&startLine.y-roadWidth<block.y){
            vec3 center=vec3(block+.5/cityScale,.03);
            float t=dot(center-eye,ray);
            if(t<dist){
                vec3 p=eye+t*ray;
                float fog=(exp(-p.z/fogDistance)-exp(-eye.z/fogDistance))/ray.z;
                fog=exp(fogDensity*fog);
                t=distance(center,p);
                beacon+=fog*pow(clamp(1.-t*3.,0.,1.),4.);
            }
        }
        
        if(dist<400.){
            return CityResult(dist,beacon,face,HIT_CITY,length(resultPos-eye),resultPos.xzy);
        }
        
        vec2 dim=step(dis.xy,dis.yx);
        dis+=dim*ris/cityScale;
        block+=dim*rs.xy/cityScale;
    }

    if(ray.z<0.){
        return CityResult(-eye.z*ri.z,beacon,0.,1.,1000.,(eye+1000.*ray).xzy);
    }
        
    return CityResult(0.,beacon,0.,NOHIT,0.,vec3(0.));
}

CityResult cityScene(vec3 ro,vec3 rd,inout vec4 returnColor){
    float dist=-(ro.y)/rd.y;// 右はri
    vec2 hit1=ro.xz+rd.xz*dist;
    // ■ シティの描画
    CityResult cityResult=cityRay(ro,rd);
    // vec3 p=ro+cityResult.distance*rd;
    vec3 p=cityResult.hitPos;

    vec2 block=floor(p.xz*cityScale)/cityScale;
    vec3 window=floor(p/windowSize);
    float x=hash1(block,window.x);
    float y=hash1(block,window.y);
    float z=hash1(block,window.z);
    vec3 color=windowColor+windowDivergence*(hash3(block)-.5);
    color*=smoothstep(.1,.9,fract(2.5*(x*y*z)));

    vec3 streetLevel=streetColor*exp(-p.y/streetDistance);
    color+=streetLevel;
    color=clamp(mix(.25*streetLevel,color,cityResult.face),0.,1.);

    float fog=(exp(-p.y/fogDistance)-exp(-ro.y/fogDistance))/rd.y;
    fog=exp(fogDensity*fog);
    color=mix(fogColor,color,fog);

    color=mix(fogColor,color,clamp(cityResult.hittedTarget,0.,1.));
    vec3 tmpBeaconColor=beaconColor;
    if(0. < startProgress && startProgress < 1.) tmpBeaconColor = beaconGreenColor; 
    color+=cityResult.beacon*tmpBeaconColor;
    returnColor=vec4(color,1.);

    // ■ タイルごとのUV生成
    vec2 flooruv=floor(hit1);
    vec2 uvOfTile=hit1-flooruv-.5;
    int celltype=getcell(flooruv.xy);

    // ■ 道の描画
    // inspired by MountainBytes: PPPP 4KiB Windows mrange & virgill https://www.shadertoy.com/view/lX2GzD
    //uvから道のもとになるlenを作る
    vec2 uvOfTileForRoad=uvOfTile;
    if(celltype==0)uvOfTileForRoad*=vec2(1.,-1.);
    uvOfTileForRoad*=step(0.,uvOfTileForRoad.x+uvOfTileForRoad.y)*2.-1.;

    float len;
    float dashed;
    float dashLineSize=.1;
    float dashLineRatio=.3;
    if(celltype==2){
        len=min(abs(uvOfTileForRoad.x),abs(uvOfTileForRoad.y));
        dashed=step(fract((uvOfTileForRoad.x+uvOfTileForRoad.y)/dashLineSize),dashLineRatio);
    }else{
        len=length(uvOfTileForRoad-.5)-.5;
        float polar=atan(uvOfTileForRoad.x-.5,uvOfTileForRoad.y-.5)/(pi*2.)+.5;
        dashed=step(fract(polar/dashLineSize*4.),dashLineRatio);
    }

    //道を描画
    float roadSize=(abs(len)-roadWidth)*iResolution.y*.1;
    float lineSize=.1;
    float clampedRoad=clamp(roadSize,0.,1.);
    if(cityResult.hittedTarget==HIT_ROAD){
        bool is1Load=false;
        
        //bpは普通の線
        //lenは点線
        float bp;
        if(is1Load){
            bp=abs(len)-.2;//1road
        }
        else{
            float centerSideLine=abs(len)-.01;
            returnColor+=vec4(roadGlow/(centerSideLine*centerSideLine)*lineSize,1.);
            bp=abs(len)-.085;//2road
            len=abs(len)-.05;//2road
        }
        
        vec3 glowIntensity=roadGlow/((len*len))*lineSize;
        returnColor+=vec4(glowIntensity*dashed,1.);
        
        float baseGlowIntensity=.25/(bp*bp)*lineSize;
        returnColor+=vec4(baseGlowIntensity*roadGlow,1.);
    }

    return cityResult;
}

Racer allRacers[RACERNUM];

struct BallResult{
    float distance;
    int racerNum;
    float distanceFromEye;
    vec3 hitPos;
};

BallResult scene(vec3 p){
    // ■ レーサーの描画
    float minD=1000.;
    int racerNum=-1;
    for(int i=0;i<RACERNUM;i++){
        float d=sdSphere(p-vec3(allRacers[i].pos.x,marbleSize,allRacers[i].pos.y),marbleSize);
        if(d<minD){
            minD=d;
            racerNum=i;
        }
    }
    return BallResult(minD,racerNum,0.,p);
}


// レイマーチングによる距離探査
BallResult rayMarch(vec3 ro, vec3 rd, inout vec3 hitPos){
    float t=0.;
    float tolerance=.001;
    // 近景
    for(int i=0;i<50;i++){
        hitPos=ro+rd*t;
        BallResult sr=scene(hitPos);
        if(sr.distance<tolerance){
            return BallResult(sr.distance,sr.racerNum,length(hitPos-ro),hitPos);
            //  vec2(t,KINKEI);// ヒット時
        }
        t+=sr.distance;
    }
    return BallResult(1000.,-1,length(hitPos-ro),hitPos);
}

BallResult ballScene(vec3 ro,vec3 rd,inout vec3 hitPos, out vec4 color){
    BallResult sr = rayMarch(ro,rd,hitPos);

    vec2 o=vec2(.001,0);   
    vec3 n=normalize(vec3(
            scene(sr.hitPos+o.xyy).distance-scene(sr.hitPos-o.xyy).distance,
            scene(sr.hitPos+o.yxy).distance-scene(sr.hitPos-o.yxy).distance,
            scene(sr.hitPos+o.yyx).distance-scene(sr.hitPos-o.yyx).distance
        ));
    // balls
    float fresnel=pow(1.-dot(-rd,n),5.);
    fresnel=mix(.5,1.,fresnel);
    
    ro=sr.hitPos+n*.0015;
    rd=reflect(rd,n);
    
    CityResult bounceCr = cityScene(ro,rd,color);
    color *=fresnel;
    return sr;
}

//    ____                               
//   / ___|__ _ _ __ ___   ___ _ __ __ _ 
//  | |   / _` | '_ ` _ \ / _ \ '__/ _` |
//  | |__| (_| | | | | | |  __/ | | (_| |
//   \____\__,_|_| |_| |_|\___|_|  \__,_|
struct Camera{
    vec3 ro;
    vec3 rd;
};

vec3 createRd(in vec2 fragCoord, vec3 forward){
    vec3 right=normalize(cross(forward,vec3(0.,1.,0.)));
    vec3 up=cross(right,forward);
    vec2 xy=2.*fragCoord-iResolution.xy;
    float zoom=3.;
    zoom*=iResolution.y;
    vec3 rd=normalize(xy.x*-right+xy.y*up+zoom*forward);
    return rd;
}

Camera makeCamera(in vec2 fragCoord, float time, bool isStarting, vec3 hashedValue, bool isFinished){

    // ■ 画面全体のUV
    vec2 realUV=(fragCoord.xy*2.-iResolution.xy)/iResolution.y;

    vec3 h=hashedValue;
    bool onBoardCamera=h.x<onBoardCameraProb;
    bool backCamera=h.y<onBoardCameraProb;
    int racerNum=int(h.z*float(RACERNUM));
    
    // オンボードカメラ
    if(!isFinished&&!isStarting&&onBoardCamera){
        float isBack=1.;
        if(backCamera)isBack=-1.;
        
        vec2 center2d=allRacers[racerNum].pos;

        vec2 dirForRacingObject=allRacers[racerNum].dirForRacingObject;

        vec3 ro=vec3(center2d.x-dirForRacingObject.x*.05*isBack,.05,center2d.y-dirForRacingObject.y*.04*isBack);

        vec2 tmpUV=vec2(-realUV.x,realUV.y);
        vec3 rd=normalize(vec3(tmpUV-vec2(0.,.5),1.));
        rd.xz*=mat2(vec2(-dirForRacingObject.y,dirForRacingObject.x),dirForRacingObject)*isBack;
        return Camera(ro,rd);
    }


    vec2 center2d=allRacers[racerNum].pos;
    if(isFinished){
        int index=int(passedCellNum(RACELAP + extraMileage));
        TruchetData data=allSkippedData[index];
        center2d=data.pos;
    } 
    

    // roの定義
    vec3 center=vec3(center2d.x,.1,center2d.y);

    vec2 m=vec2(.01*iTime,.5+sin(.02*iTime)*.2);
    if(iMouse.z>0.)m=iMouse.xy/iResolution.xy;
    m*=tau*vec2(1.,.25);
    float cameraDist=2.;
    vec3 mouseFix=vec3(
        cameraDist*sin(m.x)*sin(m.y),
        cameraDist*cos(m.y),
        cameraDist*cos(m.x)*sin(m.y));

    //　固定カメラ
    bool isKoteiCamera=backCamera&&!onBoardCamera;
    if(!isFinished&&!isStarting&&isKoteiCamera){
        int index=int(passedCellNum(allRacers[racerNum].mileage));
        TruchetData data=allSkippedData[index+1];
        center=vec3(data.pos.x,.1,data.pos.y);
        mouseFix=vec3(
            roadWidth*sin(m.x)*sin(m.y),
            roadWidth*cos(m.y),
            roadWidth*cos(m.x)*sin(m.y));
        vec3 ro = center+mouseFix;
        vec3 target =vec3(allRacers[racerNum].pos.x,marbleSize,allRacers[racerNum].pos.y);
        vec3 forward = normalize(target-ro);
        return Camera(ro,createRd(fragCoord,forward));
    }


    vec3 ro=center+mouseFix;

    if(isStarting){
        int index=int(passedCellNum(0.));
        TruchetData data=allSkippedData[index];
        center=vec3(data.pos.x,.0,data.pos.y);
        ro = center + vec3(data.dir.x,0.3,data.dir.y) + mouseFix*0.05;
    }

    // rdの定義
    vec3 forward=normalize(center-ro);
    vec3 rd=createRd(fragCoord,forward);
    
    

    return Camera(ro,rd);
}

//   __  __    _    ___ _   _ 
//  |  \/  |  / \  |_ _| \ | |
//  | |\/| | / _ \  | ||  \| |
//  | |  | |/ ___ \ | || |\  |
//  |_|  |_/_/   \_\___|_| \_|


// === CRYSTAL FAST HYBRID (inserted) ===
#ifndef RACER_RADIUS
#define RACER_RADIUS 0.12
#endif
#ifndef RACER_RADIUS2
#define RACER_RADIUS2 (RACER_RADIUS*RACERADIUS)
#endif

vec4 shadeRacersHybrid(vec2 gameUV, vec4 baseColor){
    const float R = RACER_RADIUS;
    const float R2 = R*R; // safer than macro typo
    float bestD2 = 1e9;
    vec4  bestCol = baseColor;
    bool anyHit = false;
    for (int i = 0; i < RACERNUM; ++i) {
        vec2 d = gameUV - allRacers[i].pos;
        if (abs(d.x) > R || abs(d.y) > R) continue;
        float d2 = dot(d,d);
        if (d2 <= R2 && d2 < bestD2) {
 bestD2 = d2;
 bestCol = allRacers[i].color;
 anyHit = true;
 }
    }
    //if (anyHit) {return bestCol;}
    for (int k = 0; k < RACERNUM; ++k) {
        int i = (RACERNUM-1) - k;
        if (drawCircle(gameUV, allRacers[i].pos)) return allRacers[i].color;
    }
    return baseColor;
}
// === END CRYSTAL FAST HYBRID ===
void mainImage(out vec4 fragColor,in vec2 fragCoord)
{
    vec2 floorpos=STARTPOSITION;
    vec2 initialPos=INITIALPOS;
    vec2 dir=INITIALDIR;// 初期の方向 以降最初はx=1の方向に進む前提のコード
    
    // ■ 時間の設定
    float gameSpeed = iTime * 0.75;
    float time = fract(gameSpeed / (ALLLAP)) * (ALLLAP);
    float raceTime = BEFORELAP < time ? time - BEFORELAP: 0.;
    progress = raceTime / RACELAP;
    beforeProgress = clamp(time/(BEFORELAP),0.,1.);
    startProgress = clamp((raceTime/STARTLAP),0.,1.);
    finishProgress = clamp((time-(ALLLAP-AFTERLAP-ENDLAP))/AFTERLAP,0.,1.);
    // ■ トルシェのデータの生成
    setSkipTruchetData(initialPos,dir,floorpos,time);
    
    
    // ■ レーサーの生成
    for(int i=0;i<RACERNUM;i++){
        float noiseVert=snoise(vec2(time*.01+COURCEID,float(i+1)*100.))*3.*progress-2.;
        float noiseSide=snoise(vec2(time*.1+COURCEID,float(i+1)*10.*COURCEID))*.25;
        float normalMileage = raceTime + noiseVert + extraMileage;
        normalMileage = min(normalMileage, floor(RACELAP + extraMileage));
        float normalSidePosition = noiseSide;

        float beforeMileage = float(i) / float(RACERNUM) * 0.25;
        float beforeSidePosition = (i % 2 == 0) ? 0.2 : -0.2;

        // スタート時に徐々にスタート
        float mileage = mix(beforeMileage, normalMileage, startProgress);
        float sidePosition = mix(beforeSidePosition, normalSidePosition, startProgress);

        allRacers[i]=createRacerFromTruchet(mileage,sidePosition,RACERNUMCOLOR[i],54321.);
    }
    bubbleSort(allRacers,RACERNUM);

    // ランダム値
    vec3 h=hash3(floor(vec2(time/3.))+COURCEID);
    int racerNum=int(h.z*float(RACERNUM));

    // ■ カメラの生成
    Camera camera = makeCamera(fragCoord, raceTime, startProgress*2.<1., h, 0.<finishProgress);
    vec3 ro=camera.ro;
    vec3 rd=camera.rd;

    // ■ レイキャスティング
    float dist=-(ro.y)/rd.y; // 右はri
    vec2 hit1=ro.xz+rd.xz*dist;

    //       _             _   ____                     _
    //   ___| |_ __ _ _ __| |_|  _ \ _ __ __ ___      _(_)_ __   __ _
    //  / __| __/ _` | '__| __| | | | '__/ _` \ \ /\ / / | '_ \ / _` |
    //  \__ \ || (_| | |  | |_| |_| | | | (_| |\ V  V /| | | | | (_| |
    //  |___/\__\__,_|_|   \__|____/|_|  \__,_| \_/\_/ |_|_| |_|\__, |
    //                                                          |___/

    // 空の描画
    fragColor=vec4(fogColor,1);

    // ■ シティの描画
    CityResult cityResult = cityScene(ro,rd,fragColor);
        
    // ■ レーサーの描画
    vec3 hitPos;
    vec4 bounceColor;
    BallResult sr=ballScene(ro,rd,hitPos,bounceColor);
    if(sr.racerNum!=-1&&sr.distanceFromEye<cityResult.distanceFromEye){
        fragColor=mix(bounceColor*3.,allRacers[sr.racerNum].color,.4);
        // fragColor=allRacers[sr.racerNum].color;
    }



    //       _      _                 
    //    __| | ___| |__  _   _  __ _ 
    //   / _` |/ _ \ '_ \| | | |/ _` |
    //  | (_| |  __/ |_) | |_| | (_| |
    //   \__,_|\___|_.__/ \__,_|\__, |
    //                          |___/ 

    // ■グリッドの描画
    float grid=calculateGridLine(hit1,1.,.02);
    if(ONGRID) fragColor=mix(fragColor,vec4(0., 0.0, 1.0, 1.0),grid);


    // // ■ スタートポジションは色を反転
    // if(0.<hit1.x && hit1.x<1. && 0.<hit1.y && hit1.y<1.){
    //     fragColor=vec4(1.,0.,0.,1.);
    //     return;
    //     // fragColor=1.-fragColor;
    // }

    //                       _             
    //    _____   _____ _ __| | __ _ _   _ 
    //   / _ \ \ / / _ \ '__| |/ _` | | | |
    //  | (_) \ V /  __/ |  | | (_| | |_| |
    //   \___/ \_/ \___|_|  |_|\__,_|\__, |
    //                               |___/ 



    // ■ 画面UVの作成
    vec2 uvForScreen=fragCoord/iResolution.x;
    
    // ■ mapの描画
    if(.75<uvForScreen.x && .4<uvForScreen.y){
        vec2 baseUV = (fragCoord.xy*2.-iResolution.xy+vec2(-.75,-.75)*iResolution.xy)/iResolution.x;
        vec2 gameUV=8.*baseUV+allRacers[racerNum].pos;//3dでは不要
        float mapRoadWidth=.15;
        float mapClampedRoad = getRoadOrCity(gameUV,mapRoadWidth);

        fragColor=mix(vec4(0),vec4(1),mapClampedRoad);
        
fragColor = shadeRacersHybrid(gameUV, fragColor);
}

   

    // ■　順位の描画
    if(uvForScreen.x<0.15){
        uvForScreen*=40.;
        int lineNum=int(floor((uvForScreen.y)/1.2));
        
        ivec2 index=ivec2(uvForScreen/vec2(.8,1.2));
        uvForScreen.x=mod(uvForScreen.x,.8);
        uvForScreen.y=mod(uvForScreen.y,1.2);
        int max_char = 1;
        int raceOrderInIndex=15-lineNum;
        if(0 <= raceOrderInIndex && raceOrderInIndex <= RACERNUM){
              // 順序
            if(0==index.x)fragColor=vec4(font(uvForScreen,raceOrderInIndex+1),1.);

            // 色
            if(1 == index.x){
                fragColor=allRacers[raceOrderInIndex-1].color;
            }
            // Interval表示
            // 先頭
            if(raceOrderInIndex == 1){
                if(2==index.x)fragColor=vec4(0.,0.,0.,1.);
                if(3==index.x)fragColor=vec4(0.,0.,0.,1.);
                if(4==index.x)fragColor=vec4(font(uvForScreen,45),1.);
                if(5==index.x)fragColor=vec4(font(uvForScreen,50),1.);
                if(6==index.x)fragColor=vec4(font(uvForScreen,56),1.);
            }
            else{ //　先頭以外
                float interval = allRacers[raceOrderInIndex-2].mileage - allRacers[raceOrderInIndex-1].mileage;
                if(2==index.x)fragColor=vec4(font(uvForScreen,73),1.);
                if(3==index.x)fragColor=vec4(font(uvForScreen,int(interval+1.)),1.);
                if(4==index.x)fragColor=vec4(font(uvForScreen,76),1.);
                if(5==index.x)fragColor=vec4(font(uvForScreen,int(fract(interval)*10.+1.)),1.);
                if(6==index.x)fragColor=vec4(font(uvForScreen,int(fract(interval*10.)*10.+1.)),1.);
            }

            //選択中のレーサーの色を反転
            if(raceOrderInIndex-1 == racerNum && 2 <= index.x && index.x <= 6){ 
                fragColor=1.0-fragColor;
            }

            // ラップ数表示
            if(raceOrderInIndex==0){
                raceTime=min(raceTime+extraMileage,RACELAP);
                int lap10=int(fract(raceTime*.01)*10.);
                int lap1=int(fract(raceTime*.1)*10.);
                if(0==index.x)fragColor=vec4(0.,0.,0.,1.);
                if(1==index.x)fragColor=vec4(font(uvForScreen,lap10+1),1.);
                if(2==index.x)fragColor=vec4(font(uvForScreen,lap1+1),1.);
                if(3==index.x)fragColor=vec4(font(uvForScreen,77),1.);
                int niketame=int(fract(RACELAP*.01)*10.);
                int hitoketame=int(fract(RACELAP*.1)*10.);
                if(4==index.x)fragColor=vec4(font(uvForScreen,niketame+1),1.);
                if(5==index.x)fragColor=vec4(font(uvForScreen,hitoketame+1),1.);
                if(6==index.x)fragColor=vec4(0.,0.,0.,1.);
            }


        }
    }

    // 終了後暗転
    if(0.<finishProgress){
        fragColor=mix(clamp(fragColor,0.,1.),vec4(0.),finishProgress);
        return;
    }
}
