├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── TODO ├── screen.png ├── shader.glsl ├── shader_code.h ├── src ├── gl.rs ├── gl_util.rs ├── intro.rs ├── main.rs ├── math_util.rs ├── music.rs ├── random.rs ├── shader_code.h ├── shaders.rs └── util.rs └── xargo.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/target/i686-pc-windows-msvc/debug/miniwin.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug.allowBreakpointsEverywhere": true 3 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "miniwin" 5 | version = "0.1.0" 6 | dependencies = [ 7 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "winapi" 12 | version = "0.3.8" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | dependencies = [ 15 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "winapi-i686-pc-windows-gnu" 21 | version = "0.4.0" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "winapi-x86_64-pc-windows-gnu" 26 | version = "0.4.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [metadata] 30 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 31 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 32 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 33 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "miniwin" 3 | version = "0.1.0" 4 | authors = ["SLIMPANDA\\janio "] 5 | edition = "2018" 6 | 7 | [features] 8 | logger = [] 9 | fullscreen = [] 10 | 11 | [profile.release] 12 | lto = true 13 | codegen-units = 1 # should be 1, test diff 14 | opt-level = "z" # is this highest 15 | panic = 'abort' 16 | # rustflags = ["-C", "target-cpu=native", "--emit=obj"] #??? to emit the obj files not sure about first two 17 | # rustflags = ["--emit=obj"] #??? to emit the obj files not sure about first two 18 | 19 | [profile.dev] 20 | lto = true 21 | opt-level = 0 22 | panic = "abort" 23 | 24 | [dependencies] 25 | winapi = { version = "0.3.8", features = ["winuser", "libloaderapi", "processthreadsapi", "fileapi", "handleapi", "mmreg", "mmsystem", "mmeapi" ] } 26 | #winapi = { version = "0.3.8", features = ["winuser", "libloaderapi", "processthreadsapi" ] } 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jani Peltonen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ochre 2 | This is the full source code for [ochre](https://www.pouet.net/prod.php?which=85924) 4K intro. All code is pure Rust and glsl. Please do not use this code to learn how to write *good* Rust code but you can use it to learn how to write code that compiles very *small*. 3 | 4 | ![Intro image](screen.png) 5 | 6 | A more detailed explanation about the different size optimizations can be found [here](https://www.codeslow.com/2020/07/writing-winning-4k-intro-in-rust.html) 7 | 8 | ## For easier development 9 | During development use following to enable loading shader from shader.glsl and have movable camera and logs 10 | ``` 11 | xargo run --target i686-pc-windows-msvc --features logger 12 | ``` 13 | 14 | ## For the release version 15 | 16 | First compile release version 17 | ``` 18 | xargo rustc --release --target i686-pc-windows-msvc -- --emit=obj 19 | ``` 20 | 21 | Then use crinkler to compress ( the precise path to the windows sdk will depend on your version ) 22 | ``` 23 | ..\..\..\..\..\tools\crinkler /OUT:mini.exe /SUBSYSTEM:WINDOWS miniwin.o /ENTRY:mainCRTStartup "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x86" gdi32.lib user32.lib opengl32.lib kernel32.lib winmm.lib 24 | ``` 25 | 26 | To analyze the compiled assembly code run 27 | ``` 28 | xargo rustc --release --features fullscreen --target i686-pc-windows-msvc -- --emit=asm 29 | ``` 30 | 31 | The glsl shader is compressed by the minifier using the command line 32 | ``` 33 | .\tools\shader_minifier.exe .\shader.glsl --preserve-externals --format none 34 | ``` 35 | This will create the the file ```shader_code.h``` from where you need to manually copy and paste the minified code into ```shader.rs``` 36 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | *Fill the screen 2 | *Fixed to 1 VB 3 | 4 | Use real time 5 | Finish up articel and push 6 | 7 | Add simple but more interesting pixel shader that 8 | -maintains aspect ratio 9 | -virtual size of one of the axes 10 | -understand what vars are always available 11 | 12 | -full screen version 13 | - 14 | -------------------------------------------------------------------------------- /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janiorca/sphere_dance/6db8b8c3ecc98948d14770ba21d5614cc7b71539/screen.png -------------------------------------------------------------------------------- /shader.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | const int num_spheres = 80; 3 | const float width = 1920; 4 | const float height = 1080; 5 | //const float width = 1280; 6 | //const float height = 720; 7 | 8 | uniform vec4 sp[(num_spheres+2)*2]; 9 | uniform sampler2D terrain; 10 | in vec4 gl_FragCoord; 11 | out vec4 fragColor; 12 | 13 | // prenormalized to avoid having it in the script -> made it worse?! 14 | // const vec3 sun_dir = vec3( 0.55, 0.61, 0.56 ); 15 | const float maximum_dist = 99999.0; 16 | 17 | vec2 water[4] = vec2[4]( normalize(vec2( 0.23, 0.65 )), 18 | normalize(vec2( 0.83, -0.26 )), 19 | normalize(vec2( 0.13, -0.83 )), 20 | normalize(vec2( -0.2, 0.55 ))); 21 | 22 | 23 | 24 | vec3 water_ripple( vec3 pos ) { 25 | float intensity = 0.0; 26 | float intensity2 = 0.0; 27 | for( int k=0; k< 4; k++ ) { 28 | float t = pos.x*water[k].x + pos.z*water[k].y; 29 | t = t*(4.0-(float(k)*0.51013))+sp[162].z; 30 | 31 | float mt = 1.0/(float(k)+1.0); 32 | intensity += mt*sin(t-0.3*cos(t)); 33 | intensity2 += mt*cos(t-0.3*sin(t)); 34 | } 35 | return vec3( intensity, 0.0, intensity2 ); 36 | } 37 | 38 | bool w_intersect_sphere( vec3 ray_dir, vec3 origin, vec3 sphere, float sphere_radius2, out float ott ) { 39 | // intersect with sphere 40 | vec3 origToSphere = sphere - origin; 41 | float tCA = dot( origToSphere, ray_dir); 42 | if( tCA < 0.0 ) { 43 | // ray center is towards back of ray. cant intsesect 44 | return false; 45 | } else 46 | { 47 | float dd = length(origToSphere); 48 | float distToMidpoint2 = dd*dd-tCA*tCA; 49 | if( distToMidpoint2 > sphere_radius2 ) { 50 | return false; 51 | } 52 | else { 53 | float thc = sqrt(sphere_radius2-distToMidpoint2); 54 | ott = tCA - thc; 55 | return true; 56 | } 57 | } 58 | } 59 | 60 | float get_height( vec2 pos, out float type ) { 61 | vec4 col = texture( terrain, pos/512.0 ); 62 | type = col.y; 63 | return col.x*60.0-12.1; 64 | } 65 | 66 | void intersect_box( vec3 origin, vec3 delta, out float near, out float far ) { 67 | vec3 t1 = (vec3( 0. ) - origin )/delta; 68 | vec3 t2 = (vec3( 512. ) - origin )/delta; 69 | near = max( min( t1.x, t2.x ), min( t1.z, t2.z ) ); 70 | far = min( max( t1.x, t2.x ), max( t1.z, t2.z ) ); 71 | } 72 | 73 | bool cast_ray( vec3 origin, vec3 delta, out float t, out vec3 col, out vec3 normal, out float refrac, out float type ) 74 | { 75 | float near_t, far_t; 76 | intersect_box( origin,delta, near_t, far_t ); 77 | 78 | if( far_t < near_t ) { 79 | return false; 80 | } 81 | 82 | float skip_t = max( 0., near_t ); 83 | origin = origin + skip_t*delta; 84 | 85 | // Setup stepper vars 86 | vec2 tmax, tdelta, grid_step; 87 | grid_step = sign( delta.xz ); 88 | tdelta = 1.0 / delta.xz * grid_step; // inverts the sign for negs, making it equivalent to abs 89 | tmax = tdelta * (max(grid_step,0) - fract(origin.xz)*grid_step); 90 | // Seems to be increasing the size of the final 91 | if( any( isinf( tmax ) || isinf( tdelta ) ) ) { 92 | return false; 93 | } 94 | 95 | vec2 ip = floor( origin.xz ); 96 | float next_y = origin.y; 97 | float old_height = get_height( ip, type ); 98 | refrac = 1.2; 99 | 100 | for( t=0.0;t= tmax.y)); 102 | t = dot(tmax,or); 103 | float y = origin.y + delta.y * t; 104 | ip = ip + grid_step*or; 105 | tmax = tmax + tdelta*or; 106 | 107 | // check exit height 108 | if( old_height > y ) { 109 | col = vec3( 0.2, 0.071, .01 ) + step( 27, old_height ) * vec3( 0.1, -0.06, 0 ); 110 | normal = vec3( 0, 1.0, 0 ); 111 | 112 | // work out precise t ( maybe precision errors when delta.y is near 0) 113 | t = ( old_height - origin.y ) / delta.y; 114 | // t += skip_t; // in all code we should always start inside the map so skip_t should never be required 115 | return true; 116 | } 117 | 118 | // check entry height to next pos 119 | old_height = get_height( ip, type); 120 | if( old_height > y ) { 121 | refrac =1.5; 122 | col = vec3( .2, .2, .2 ) + step( 40, old_height ) * vec3( 0., -.03, -.1 ); 123 | 124 | if( type == 1.0 ) { 125 | float threshold = (0.6335-0.01)*60.0 - 12.1; 126 | if( y < threshold ) { 127 | type = 0.0; 128 | } 129 | } 130 | //t += skip_t; 131 | normal = vec3( -grid_step.x*or.x, 0, -grid_step.y*or.y ); 132 | return true; 133 | } 134 | } 135 | return false; 136 | } 137 | 138 | 139 | float fresnel( float n2, vec3 normal, vec3 incident ) 140 | { 141 | // Schlick aproximation 142 | float r0 = (1.0-n2) / (1.0+n2); // r0 could be precalced. Fresnel is always with respect to air 143 | r0 *= r0; 144 | float cosX = -dot(normal, incident); 145 | float x = 1.0-cosX; 146 | float ret = r0+(1.0-r0)*x*x*x*x*x; 147 | return ret; 148 | } 149 | 150 | const vec3 absorption_coeff = vec3( 0.000005, 0.000015, 0.00027 )*15.0; 151 | const vec3 scattering_coeff = vec3( 0.00015, 0.00015, 0.00027 )*15.0; 152 | 153 | 154 | vec3 extinction( float dist ) { 155 | return exp( -dist*( absorption_coeff + scattering_coeff ) ); 156 | } 157 | 158 | vec3 in_scatter( float dist, float cos_angle ) { 159 | float rayleigh_scatter = .0003 / 16.0*3.14159* ( 1.0 + cos_angle*cos_angle ); 160 | vec3 rayleigh_coeff = 1.0 / ( absorption_coeff + scattering_coeff ) * ( 1.0-exp( -dist*( scattering_coeff ) ) ); 161 | 162 | float mie_g = 0.476; 163 | vec3 mie_scatter = vec3( 0.0020, 0.0008, 0.0002 ) * ( 1.0 - mie_g )*( 1.0 - mie_g ) / ( 4.0 * 3.14159 * pow( ( 1.0 + mie_g*mie_g - 2.0 * mie_g *cos_angle ), 1.5 ) ); 164 | float mie_coeff = 20.0 / ( absorption_coeff.x + scattering_coeff.x ) * ( 1.0-exp( -dist*( scattering_coeff.x ) ) ); 165 | return rayleigh_scatter*rayleigh_coeff+mie_scatter*mie_coeff; 166 | } 167 | 168 | 169 | void main() 170 | { 171 | vec3 sun_dir = normalize( vec3( 1.0, 1.10, 1.0 )); 172 | 173 | // calculate normalized screen pos with center at 0,0 extending width/height,1 174 | vec2 screen_pos_2d = 2.0*(gl_FragCoord.xy/height) - vec2( width/height, 1.0 ); 175 | 176 | // establish the 3d normalized 3d position, camera is at 0,0,0, ray is towards screen_pos, depth 177 | // vec3 camera_tgt_3d = vec3( screen_pos_2d, -2.0 ); 178 | //vec3 camera_pos_3d = vec3( 0., 0., 0.); // no need to track as it is at 0,0,0 179 | vec4 co = cos( sp[ 161 ] ); // a= co.y c = co.x 180 | vec4 si = sin( sp[ 161 ] ); // b= si.y d = si.x 181 | mat3 rot_m = mat3( co.y, 0, -si.y, 182 | -si.x*si.y, co.x, -si.x*co.y, 183 | co.x*si.y, si.x, co.y*co.x ); 184 | // no roll at the moment 185 | // float _angle3 = sp[ 161 ].z; 186 | // mat3 roll = mat3( cos(_angle3), -sin( _angle3) , 0 , 187 | // sin(_angle3), cos(_angle3),0 , 188 | // 0, 0, 1 ); 189 | // rot_m = rot_m * tilt_m * roll; 190 | 191 | // vec3 origin = rot_m*camera_pos_3d; no need to rotate origin, Its at 0,0,0 192 | vec3 dest = rot_m*vec3( screen_pos_2d, -2.0 ); 193 | // vec3 dest = rot_m*camera_tgt_3d; 194 | 195 | vec3 origin = sp[160].xyz; // camera at translated origin 196 | dest += origin; 197 | 198 | vec3 ray_dir = normalize( dest - origin ); 199 | 200 | float contribution = 1.0; 201 | vec3 final_color = vec3( 0); 202 | 203 | for( int bounce =2; bounce >0 ; bounce -- ) { 204 | vec3 new_ray_dir; 205 | vec3 norm; 206 | vec3 pos; 207 | vec3 diffuseCol; 208 | float refractive_index; 209 | float reflectance = 0.0; 210 | float current_t = maximum_dist; 211 | 212 | for( int idx=0; idx < num_spheres; idx++ ) { 213 | float n_t; // For some reason I cant pass current_t as out var into the func. Somehow the compiler seems to optimize out 214 | // the preceeding assignment if I do 215 | if( w_intersect_sphere( ray_dir, origin, sp[idx*2].xyz, sp[idx*2].w, n_t) ) { 216 | if( n_t < current_t ) { 217 | current_t = n_t; 218 | pos = origin + current_t*ray_dir; 219 | norm = normalize( pos-sp[idx*2].xyz); 220 | diffuseCol = sp[idx*2+1].xyz; // vec3( 0.02, .02, 0.02 ); 221 | refractive_index = sp[idx*2+1].w; // 1.3171; 222 | reflectance = fresnel( refractive_index, norm, ray_dir); 223 | } 224 | } 225 | } 226 | 227 | //Check if we hit the sceneary 228 | float grid_t; 229 | vec3 diffuseCol2; 230 | vec3 norm2; 231 | float type; 232 | if( cast_ray(origin, ray_dir, grid_t, diffuseCol2, norm2, refractive_index, type ) ) { 233 | // hit the scenery, if it is closer than the sphere this overrides 234 | if( grid_t < current_t ) { 235 | current_t = grid_t; 236 | diffuseCol = diffuseCol2; 237 | norm = norm2; 238 | pos = origin + ray_dir * current_t*0.9999; 239 | reflectance = fresnel( refractive_index, norm, ray_dir); 240 | } 241 | } else if( ray_dir.y < 0.0 && current_t == maximum_dist) { 242 | current_t = ( -10.5-origin.y ) / ray_dir.y;// ground_plane_intersect( ray_dir, origin , -0.5 ); 243 | } 244 | 245 | grid_t = ( -0.5-origin.y ) / ray_dir.y;// ground_plane_intersect( ray_dir, origin , -0.5 ); 246 | if( ray_dir.y < 0.0 && grid_t <= current_t ) { 247 | pos = origin + ray_dir * grid_t*0.9999; 248 | // divide angle effect by distance to avoid grazing angle problems where the ripple would put the camera direction 'below' the water normal 249 | norm = vec3( 0.0, 1.0f, 0.0f ) + water_ripple( pos )*0.03/grid_t; 250 | norm = normalize(norm); 251 | 252 | reflectance = fresnel( 1.1, norm, ray_dir); 253 | 254 | //bend and rethrow ray underwater 255 | vec3 uw_dir = refract( ray_dir, norm, 1.-reflectance); 256 | diffuseCol = vec3( 0.05, 0.05, 0.15 ); 257 | if( cast_ray(pos, uw_dir*100, grid_t, diffuseCol2, norm2, refractive_index, type ) ) { 258 | diffuseCol += diffuseCol2 * exp( -grid_t*40.0 ); 259 | } 260 | } 261 | new_ray_dir = reflect( normalize( ray_dir ), norm ); 262 | 263 | if( current_t >= maximum_dist || bounce == 1 ) { 264 | final_color += in_scatter( current_t, dot( sun_dir,ray_dir) ) * contribution; 265 | break; 266 | } 267 | 268 | // // light the point 269 | // Is the light shadowed 270 | bool in_shade = cast_ray( pos, sun_dir, grid_t, diffuseCol2, norm2, refractive_index, type ); 271 | if( !in_shade ) 272 | { 273 | for( int idx=0; idx < num_spheres; idx++ ) 274 | { 275 | if( w_intersect_sphere( sun_dir, pos, sp[idx*2].xyz, sp[idx*2].w, grid_t ) ) { 276 | in_shade = true; 277 | break; 278 | } 279 | } 280 | } 281 | 282 | // reusing diffusecol2 for poitn col to avoid declaring extra var 283 | if( !in_shade) 284 | { 285 | float diffuse = dot( sun_dir, norm ); 286 | vec3 halfway = normalize( sun_dir-ray_dir ); // halfwar between vectors pointing towards camera and sun 287 | float specular = pow( dot( norm, halfway ), 121.0 ); 288 | specular = clamp( specular, 0.0, 1.0 ); 289 | diffuseCol2 = vec3(specular) + diffuseCol * diffuse; 290 | } else { 291 | diffuseCol2 = diffuseCol* 0.02; 292 | } 293 | // attenuate 294 | diffuseCol2 *= extinction( current_t ); 295 | diffuseCol2 += in_scatter( current_t, dot( sun_dir,ray_dir) ); 296 | 297 | final_color += diffuseCol2 * contribution * ( 1.0 - reflectance ); 298 | contribution = contribution * reflectance; 299 | ray_dir = new_ray_dir; 300 | origin = pos; 301 | 302 | } 303 | 304 | // vignetting 305 | // float cut_fraction = min( sp[162].x, sp[162].y ); 306 | // if( cut_fraction <= 18 ) { 307 | // cut_fraction = 1.-cut_fraction/8.; 308 | // } else { 309 | // cut_fraction = 0.0; 310 | // } 311 | // cut_fraction = 0.0; 312 | // float dist = length( vec2( screen_pos_2d.x*(height/width), screen_pos_2d.y) ); 313 | // float vignetting_level = min( 1.0, smoothstep( 0.95*(1.-cut_fraction/26.0), 1.31, dist )*0.6 + cut_fraction ); 314 | 315 | // vec3 vfcolor = mix( final_color, vec3(0), vignetting_level ); 316 | 317 | // //fragColor = vec4( pow( vfcolor, vec3(1.0 / 2.2) ), 1. ); 318 | fragColor = vec4( pow( final_color, vec3(1.0 / 2.2) ), 1. ); 319 | } 320 | -------------------------------------------------------------------------------- /shader_code.h: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | const int f=80;const float v=1280,z=720;uniform vec4 sp[(f+2)*2];uniform sampler2D terrain;in vec4 gl_FragCoord;out vec4 fragColor;const float e=99999.;vec2 p[4]=vec2[4](normalize(vec2(.23,.65)),normalize(vec2(.83,-.26)),normalize(vec2(.13,-.83)),normalize(vec2(-.2,.55)));vec3 t(vec3 v){float f=0.,i=0.;for(int z=0;z<4;z++){float e=v.x*p[z].x+v.z*p[z].y;e=e*(4.-float(z)*.51013)+sp[162].z;float s=1./(float(z)+1.);f+=s*sin(e-.3*cos(e));i+=s*cos(e-.3*sin(e));}return vec3(f,0.,i);}bool t(vec3 f,vec3 e,vec3 v,float z,out float i){vec3 y=v-e;float s=dot(y,f);if(s<0.)return false;else{float n=length(y),o=n*n-s*s;if(o>z)return false;else{float t=sqrt(z-o);i=s-t;return true;}}}float t(vec2 f,out float v){vec4 s=texture(terrain,f/512.);v=s.y;return s.x*60.-12.1;}void t(vec3 f,vec3 e,out float v,out float i){vec3 s=(vec3(0.)-f)/e,o=(vec3(512.)-f)/e;v=max(min(s.x,o.x),min(s.z,o.z));i=min(max(s.x,o.x),max(s.z,o.z));}bool t(vec3 v,vec3 f,out float i,out vec3 e,out vec3 o,out float z,out float s){float y,n;t(v,f,y,n);if(n=m.y));i=dot(m,k);float g=v.y+f.y*i;c=c+d*k;m=m+r*k;if(u>g)return e=vec3(.2,.071,.01)+step(27,u)*vec3(.1,-.06,0),z=1.2,o=vec3(0,1.,0),i=(u-v.y)/f.y,true;u=t(c,s);if(u>g){z=1.5;e=vec3(.2,.2,.2)+step(40,u)*vec3(0.,-.03,-.1);if(s==1.){float a=25.31;if(g0;u--){vec3 p,g,a,b;float w,l=0.,C=e;for(int F=0;F=e){c+=n(C,dot(s,r))*k;break;}bool Z=t(a,s,F,D,q,w,h);if(!Z){for(int Y=0;Y GLuint { 139 | mem::transmute::<_, extern "system" fn(GLint) -> GLuint>(*GL_API.get_unchecked(wglSwapIntervalIdx as usize))(interval) 140 | } 141 | 142 | 143 | pub unsafe fn CreateProgram() -> GLuint { 144 | mem::transmute::<_, extern "system" fn() -> GLuint>(*GL_API.get_unchecked(CreateProgramIdx as usize))() 145 | } 146 | 147 | pub unsafe fn LinkProgram(program: GLuint) -> () { 148 | mem::transmute::<_, extern "system" fn(GLuint) -> ()>(*GL_API.get_unchecked(LinkProgramIdx as usize))(program) 149 | } 150 | 151 | #[cfg(feature = "logger")] 152 | pub unsafe fn GetProgramiv(program: GLuint, pname: GLenum, params: *mut GLint) -> () { 153 | mem::transmute::<_, extern "system" fn(GLuint, GLenum, *mut GLint) -> ()>(*GL_API.get_unchecked(GetProgramivIdx as usize))(program, pname, params) 154 | } 155 | 156 | pub unsafe fn UseProgram(program: GLuint) -> () { 157 | mem::transmute::<_, extern "system" fn(GLuint) -> ()>(*GL_API.get_unchecked(UseProgramIdx as usize))(program) 158 | } 159 | 160 | pub unsafe fn AttachShader(program: GLuint, shader: GLuint) -> () { 161 | mem::transmute::<_, extern "system" fn(GLuint, GLuint) -> ()>(*GL_API.get_unchecked(AttachShaderIdx as usize))(program, shader) 162 | } 163 | 164 | pub unsafe fn DetachShader(program: GLuint, shader: GLuint) -> () { 165 | mem::transmute::<_, extern "system" fn(GLuint, GLuint) -> ()>(*GL_API.get_unchecked(DetachShaderIdx as usize))(program, shader) 166 | } 167 | 168 | pub unsafe fn CreateShader(type_: GLenum) -> GLuint { 169 | mem::transmute::<_, extern "system" fn(GLenum) -> GLuint>(*GL_API.get_unchecked(CreateShaderIdx as usize))(type_) 170 | } 171 | 172 | pub unsafe fn ClearBufferfv(buffer: GLenum, drawbuffer: GLint, value: *const GLfloat) -> () { 173 | mem::transmute::<_, extern "system" fn(GLenum, GLint, *const GLfloat) -> ()>(*GL_API.get_unchecked(ClearBufferfvIdx as usize))(buffer, drawbuffer, value) 174 | } 175 | 176 | pub unsafe fn ShaderSource(shader: GLuint, count: GLsizei, string: *const *const GLchar, length: *const GLint) -> () { 177 | mem::transmute::<_, extern "system" fn(GLuint, GLsizei, *const *const GLchar, *const GLint) -> ()>(*GL_API.get_unchecked(ShaderSourceIdx as usize))(shader, count, string, length) 178 | } 179 | 180 | pub unsafe fn CompileShader(shader: GLuint) -> () { 181 | mem::transmute::<_, extern "system" fn(GLuint) -> ()>(*GL_API.get_unchecked(CompileShaderIdx as usize))(shader) 182 | } 183 | 184 | #[cfg(feature = "logger")] 185 | pub unsafe fn GetShaderiv(shader: GLuint, pname: GLenum, params: *mut GLint) -> () { 186 | mem::transmute::<_, extern "system" fn(GLuint, GLenum, *mut GLint) -> ()>(*GL_API.get_unchecked(GetShaderivIdx as usize))(shader, pname, params) 187 | } 188 | 189 | #[cfg(feature = "logger")] 190 | pub unsafe fn GetShaderInfoLog(shader: GLuint, bufSize: GLsizei, length: *mut GLsizei, infoLog: *mut GLchar) -> () { 191 | mem::transmute::<_, extern "system" fn(GLuint, GLsizei, *mut GLsizei, *mut GLchar) -> ()>(*GL_API.get_unchecked(GetShaderInfoLogIdx as usize))(shader, bufSize, length, infoLog) 192 | } 193 | 194 | #[cfg(feature = "logger")] 195 | pub unsafe fn GetProgramInfoLog(program: GLuint, bufSize: GLsizei, length: *mut GLsizei, infoLog: *mut GLchar) -> () { 196 | mem::transmute::<_, extern "system" fn(GLuint, GLsizei, *mut GLsizei, *mut GLchar) -> ()>(*GL_API.get_unchecked(GetProgramInfoLogIdx as usize))(program, bufSize, length, infoLog) 197 | } 198 | 199 | pub unsafe fn GenVertexArrays(n: GLsizei, arrays: *mut GLuint) -> () { 200 | mem::transmute::<_, extern "system" fn(GLsizei, *mut GLuint) -> ()>(*GL_API.get_unchecked(GenVertexArraysIdx as usize))(n, arrays) 201 | } 202 | 203 | pub unsafe fn BindVertexArray(array: GLuint) -> () { 204 | mem::transmute::<_, extern "system" fn(GLuint) -> ()>(*GL_API.get_unchecked(BindVertexArrayIdx as usize))(array) 205 | } 206 | 207 | pub unsafe fn GenBuffers(n: GLsizei, buffers: *mut GLuint) -> () { 208 | mem::transmute::<_, extern "system" fn(GLsizei, *mut GLuint) -> ()>(*GL_API.get_unchecked(GenBuffersIdx as usize))(n, buffers) 209 | } 210 | 211 | pub unsafe fn BindBuffer(target: GLenum, buffer: GLuint) -> () { 212 | mem::transmute::<_, extern "system" fn(GLenum, GLuint) -> ()>(*GL_API.get_unchecked(BindBufferIdx as usize))(target, buffer) 213 | } 214 | 215 | pub unsafe fn BufferData(target: GLenum, size: GLsizeiptr, data: *const CVoid, usage: GLenum) -> () { 216 | mem::transmute::<_, extern "system" fn(GLenum, GLsizeiptr, *const CVoid, GLenum) -> ()>(*GL_API.get_unchecked(BufferDataIdx as usize))(target, size, data, usage) 217 | } 218 | 219 | pub unsafe fn EnableVertexAttribArray(index: GLuint) -> () { 220 | mem::transmute::<_, extern "system" fn(GLuint) -> ()>(*GL_API.get_unchecked(EnableVertexAttribArrayIdx as usize))(index) 221 | } 222 | 223 | pub unsafe fn VertexAttribPointer(index: GLuint, size: GLint, type_: GLenum, normalized: GLboolean, stride: GLsizei, pointer: *const CVoid) -> () { 224 | mem::transmute::<_, extern "system" fn(GLuint, GLint, GLenum, GLboolean, GLsizei, *const CVoid) -> ()>(*GL_API.get_unchecked(VertexAttribPointerIdx as usize))(index, size, type_, normalized, stride, pointer) 225 | } 226 | 227 | pub unsafe fn DrawArrays(mode: GLenum, first: GLint, count: GLsizei) -> () { 228 | mem::transmute::<_, extern "system" fn(GLenum, GLint, GLsizei) -> ()>(*GL_API.get_unchecked(DrawArraysIdx as usize))(mode, first, count) 229 | } 230 | 231 | pub unsafe fn Recti(x1: GLint, y1: GLint, x2: GLint, y2: GLint ) -> () { 232 | mem::transmute::<_, extern "system" fn(GLint, GLint, GLint, GLint) -> ()>(*GL_API.get_unchecked(RectiIdx as usize))(x1,y1,x2,y2) 233 | } 234 | 235 | pub unsafe fn GetUniformLocation(program: GLuint, name: *const GLchar) -> GLint { 236 | mem::transmute::<_, extern "system" fn(GLuint, *const GLchar) -> GLint>(*GL_API.get_unchecked(GetUniformLocationIdx as usize))(program, name) 237 | } 238 | 239 | pub unsafe fn Uniform1f(location: GLint, v0: GLfloat) -> () { 240 | mem::transmute::<_, extern "system" fn(GLint, GLfloat) -> ()>(*GL_API.get_unchecked(Uniform1fIdx as usize))(location, v0) 241 | } 242 | 243 | pub unsafe fn Uniform4fv(location: GLint, count: GLsizei, value: *const GLfloat) -> () { 244 | mem::transmute::<_, extern "system" fn(GLint, GLsizei, *const GLfloat) -> ()>(*GL_API.get_unchecked(Uniform4fvIdx as usize))(location, count, value) 245 | } 246 | 247 | pub unsafe fn GenTextures(n: GLsizei, textures: *mut GLuint) -> () { 248 | mem::transmute::<_, extern "system" fn(GLsizei, *mut GLuint) -> ()>(*GL_API.get_unchecked(GenTexturesIdx as usize))(n, textures) 249 | } 250 | 251 | pub unsafe fn BindTexture(target: GLenum, texture: GLuint) -> () { 252 | mem::transmute::<_, extern "system" fn(GLenum, GLuint) -> ()>(*GL_API.get_unchecked(BindTextureIdx as usize))(target, texture) 253 | } 254 | 255 | pub unsafe fn TexImage2D(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, border: GLint, format: GLenum, type_: GLenum, pixels: *const CVoid) -> () { 256 | mem::transmute::<_, extern "system" fn(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLenum, GLenum, *const CVoid) -> ()>(*GL_API.get_unchecked(TexImage2DIdx as usize))(target, level, internalformat, width, height, border, format, type_, pixels) 257 | } 258 | 259 | pub unsafe fn TexParameteri(target: GLenum, pname: GLenum, param: GLint) -> () { 260 | mem::transmute::<_, extern "system" fn(GLenum, GLenum, GLint) -> ()>(*GL_API.get_unchecked(TexParameteriIdx as usize))(target, pname, param) 261 | } 262 | 263 | pub unsafe fn ActiveTexture(texture: GLenum) -> () { 264 | mem::transmute::<_, extern "system" fn(GLenum) -> ()>(*GL_API.get_unchecked(ActiveTextureIdx as usize))(texture) 265 | } 266 | 267 | pub unsafe fn ListBase(index: GLuint) -> () { 268 | mem::transmute::<_, extern "system" fn(GLuint) -> ()>(*GL_API.get_unchecked(ListBaseIdx as usize))(index) 269 | } 270 | 271 | pub unsafe fn CallLists(count: GLsizei, vtype: GLenum, string: *const CVoid ) -> () { 272 | mem::transmute::<_, extern "system" fn(GLsizei, GLenum, *const CVoid ) -> ()>(*GL_API.get_unchecked(CallListsIdx as usize))(count, vtype, string) 273 | } 274 | 275 | pub unsafe fn RasterPos2f(x: GLfloat, y: GLfloat ) -> () { 276 | mem::transmute::<_, extern "system" fn(GLfloat, GLfloat ) -> ()>(*GL_API.get_unchecked(RasterPos2fIdx as usize))(x,y) 277 | } 278 | 279 | pub fn init() { 280 | let handle : HMODULE; 281 | unsafe { handle = LoadLibraryA( "Opengl32.dll\0".as_ptr() as *const i8); } 282 | for &(index, name) in LOAD_DESC { 283 | unsafe { 284 | let mut prc = wglGetProcAddress(name.as_ptr() as *const i8) as usize; 285 | if prc == 0 { 286 | prc = GetProcAddress( handle, name.as_ptr() as *const i8 ) as usize; 287 | } 288 | *GL_API.get_unchecked_mut( index as usize ) = prc; 289 | } 290 | } 291 | } -------------------------------------------------------------------------------- /src/gl_util.rs: -------------------------------------------------------------------------------- 1 | use super::gl; 2 | 3 | pub fn program_from_shaders( vtx_shader : gl::GLuint, frag_shader : gl::GLuint, error_dest : &mut [i8] ) -> Option { 4 | let program_id; 5 | let mut success: gl::GLint = 1; 6 | unsafe { 7 | program_id = gl::CreateProgram(); 8 | gl::AttachShader(program_id, vtx_shader ); 9 | gl::AttachShader(program_id, frag_shader ); 10 | gl::LinkProgram(program_id); 11 | 12 | #[cfg(feature = "logger")] 13 | { 14 | gl::DetachShader(program_id, vtx_shader ); 15 | gl::DetachShader(program_id, frag_shader ); 16 | gl::GetProgramiv(program_id, gl::LINK_STATUS, &mut success); 17 | if success == 0 { 18 | gl::GetProgramInfoLog( program_id, error_dest.len() as i32, 0 as *mut _, error_dest.as_mut_ptr() as *mut u8 ); 19 | return None; 20 | } 21 | } 22 | } 23 | return Some( program_id ); 24 | } 25 | 26 | pub fn shader_from_source( shader_source : *const u8, kind: gl::GLenum, error_dest : &mut [i8] ) -> Option { 27 | let id; 28 | let mut success: gl::GLint = 1; 29 | unsafe { 30 | id = gl::CreateShader(kind); 31 | gl::ShaderSource(id, 1, &shader_source, 0 as *const _); 32 | gl::CompileShader(id); 33 | 34 | #[cfg(feature = "logger")] 35 | { 36 | gl::GetShaderiv(id, gl::COMPILE_STATUS, &mut success); 37 | if success == 0 { 38 | gl::GetShaderInfoLog( id, error_dest.len() as i32, 0 as *mut _, error_dest.as_mut_ptr() as *mut gl::GLchar ); 39 | return None; 40 | } 41 | } 42 | } 43 | return Some( id ); 44 | } -------------------------------------------------------------------------------- /src/intro.rs: -------------------------------------------------------------------------------- 1 | use super::math_util; 2 | use super::gl; 3 | use super::gl_util; 4 | use super::random; 5 | use core::arch::x86; 6 | 7 | use gl::CVoid; 8 | use core::mem::{size_of,transmute}; 9 | use core::ops::{Add,Sub,Mul}; 10 | 11 | // Floating point constants picked for compressibility 12 | pub const FP_0_01 :f32 = 0.0100097656f32; // 0.01 13 | pub const FP_0_02 :f32 = 0.0200195313f32; // 0.02f 0x3ca40000 14 | pub const FP_0_05 :f32 = 0.0500488281f32; // 15 | pub const FP_0_20 : f32 = 0.2001953125f32; 16 | pub const FP_1_32 : f32 = 1.3203125000f32; // 1.32f 0x3fa90000 17 | pub const FP_1_54 : f32 = 1.5390625000f32; 18 | 19 | // The dynamic part of the world is 80 spheres + camera and light 20 | pub const CAMERA_POS_IDX : usize = 80*2; 21 | pub const CAMERA_ROT_IDX : usize = 80*2+1; 22 | pub const CAMERA_CUT_INFO : usize = 80*2+2; 23 | pub const num_spheres : usize = 80; 24 | pub const sphere_extras : usize = 2; 25 | 26 | static mut shader_prog : gl::GLuint = 0; 27 | static mut vertex_array_id : gl::GLuint = 0; 28 | 29 | static mut rng : random::Rng = random::Rng{seed: core::num::Wrapping(21431249)}; 30 | 31 | static mut global_spheres: [ [ f32; 4]; (num_spheres+sphere_extras)*2] = [ [ 0f32; 4]; (num_spheres+sphere_extras)*2 ]; 32 | 33 | fn smooth( pixels: &mut [ f32; 512*513*4 ]) { 34 | unsafe{ 35 | let mut xy = 0; 36 | loop{ 37 | let offset = xy*4; 38 | let mut val = *pixels.get_unchecked( offset ); 39 | val += pixels.get_unchecked( offset+4 ); 40 | val += pixels.get_unchecked( offset+2048 ); 41 | val += pixels.get_unchecked( offset+2052 ); 42 | *pixels.get_unchecked_mut( offset ) = val / 4.0; 43 | xy += 1; 44 | if xy == 511*511 { break; } 45 | } 46 | } 47 | } 48 | 49 | static mut src_terrain : [ f32; 512*513*4 ] = [ 0.0; 512*513*4 ]; 50 | static mut tex_buffer_id : gl::GLuint = 0; 51 | 52 | #[cfg(feature = "logger")] 53 | static mut glbl_shader_code : [ u8;25000] = [0; 25000]; 54 | 55 | static mut old_x : i32 = 0; 56 | static mut old_y : i32 = 0; 57 | static mut moving_camera : bool = false; 58 | static mut rotating_camera : bool = false; 59 | 60 | static mut camera_velocity : [ f32; 4] = [ 0.0; 4]; 61 | static mut camera_rot_speed : [ f32; 4] = [ 0.0; 4]; 62 | 63 | static mut camera_mode : u32 = 0; 64 | static mut sphere_scale : f32 = 0.0; 65 | 66 | #[cfg(feature = "logger")] 67 | pub fn set_pos( x: i32, y: i32, ctrl : bool ) { 68 | unsafe{ 69 | if moving_camera { 70 | if ctrl{ 71 | global_spheres[ CAMERA_POS_IDX ][ 1 ] += ( y-old_y) as f32 / 32.0; 72 | } else { 73 | global_spheres[ CAMERA_POS_IDX ][ 0 ] += ( x-old_x) as f32 / 32.0; 74 | global_spheres[ CAMERA_POS_IDX ][ 2 ] += ( y-old_y) as f32 / 32.0; 75 | } 76 | } else if rotating_camera { 77 | global_spheres[ CAMERA_ROT_IDX ][ 0 ] += ( y-old_y) as f32 / 1024.0; 78 | global_spheres[ CAMERA_ROT_IDX ][ 1 ] += ( x-old_x) as f32 / 1024.0; 79 | } 80 | old_x = x; 81 | old_y = y; 82 | } 83 | } 84 | 85 | #[cfg(feature = "logger")] 86 | pub fn rbutton_down( x: i32, y: i32 ) { 87 | unsafe{ 88 | old_x = x; 89 | old_y = y; 90 | moving_camera = false; 91 | rotating_camera = true; 92 | } 93 | } 94 | 95 | #[cfg(feature = "logger")] 96 | pub fn rbutton_up( ) { 97 | setup_random_camera(); 98 | unsafe{ 99 | rotating_camera = false; 100 | } 101 | } 102 | 103 | #[cfg(feature = "logger")] 104 | pub fn lbutton_down( x: i32, y: i32 ) { 105 | unsafe{ 106 | old_x = x; 107 | old_y = y; 108 | moving_camera = true; 109 | rotating_camera = false; 110 | } 111 | } 112 | 113 | #[cfg(feature = "logger")] 114 | pub fn lbutton_up( ) { 115 | unsafe{ 116 | moving_camera = false; 117 | } 118 | unsafe{ super::log!( "Camera: ", global_spheres[ CAMERA_POS_IDX ][ 0 ], global_spheres[ CAMERA_POS_IDX ][ 1 ], global_spheres[ CAMERA_POS_IDX ][ 2 ]); } 119 | } 120 | 121 | static mut r3_pos : usize = 0; 122 | 123 | fn set_r3( dest : &mut[ f32 ; 4 ], crng : &mut random::Rng, a: f32, b: f32, c: f32, offset: f32 ) { 124 | // tried turning into a loop -> crinkled version grew 60bytes! 125 | let x = crng.next_f32(); 126 | let z = crng.next_f32(); 127 | dest[ 0 ] = (x-offset)*a; 128 | dest[ 1 ] = (crng.next_f32()-offset)*b; 129 | dest[ 2 ] = (z-offset)*c; 130 | unsafe{ 131 | // we only ever calculate the position scaled by 512 ( by the unoffset values ) 132 | r3_pos = (((z*512f32) as usize *512)+(x*512f32) as usize)*4; 133 | } 134 | } 135 | 136 | static mut sphere_delta : f32 = 0.0; 137 | fn set_sphere_positions(now: f32) -> ( ) { 138 | let mut rng_terrain : random::Rng = random::Rng{seed: core::num::Wrapping(7923129)}; 139 | 140 | let mut idx = 0; 141 | unsafe{ 142 | let mut offset = math_util::sin( (now-sphere_delta)*0.02 )*sphere_scale; 143 | if offset < 0.0 { 144 | offset = -offset; 145 | } 146 | loop { 147 | loop{ 148 | let y_offset = offset; 149 | set_r3( global_spheres.get_unchecked_mut(idx*2), &mut rng_terrain,512f32,512f32,512f32, 0.0 ); 150 | if *src_terrain.get_unchecked( r3_pos ) > 0.3f32 { 151 | global_spheres.get_unchecked_mut(idx*2)[ 1 ] = *src_terrain.get_unchecked( r3_pos )*60.0-12.1 + y_offset; 152 | global_spheres.get_unchecked_mut(idx*2)[ 3 ] = 18.0f32; 153 | global_spheres.get_unchecked_mut(idx*2+1)[ 0 ] = FP_0_02; 154 | global_spheres.get_unchecked_mut(idx*2+1)[ 1 ] = FP_0_02; 155 | global_spheres.get_unchecked_mut(idx*2+1)[ 2 ] = FP_0_02; 156 | global_spheres.get_unchecked_mut(idx*2+1)[ 3 ] = FP_1_32; 157 | break; 158 | } 159 | offset *= 1.003; 160 | } 161 | idx += 1; 162 | if idx == num_spheres { break;} 163 | } 164 | } 165 | } 166 | 167 | pub fn prepare() -> () { 168 | let mut error_message : [i8;100] = [ 0; 100]; 169 | let vtx_shader_src : &'static str = "#version 330 core 170 | layout (location = 0) in vec3 Pos; 171 | void main() 172 | { 173 | gl_Position = vec4(Pos, 1.0); 174 | }\0"; 175 | 176 | let spheres : &mut[ [ f32; 4]; (num_spheres+sphere_extras)*2]; 177 | unsafe{ 178 | spheres = &mut global_spheres; 179 | } 180 | 181 | let vtx_shader : u32; 182 | let frag_shader : u32; 183 | unsafe{ super::log!( "Load shader !"); }; 184 | 185 | #[cfg(not(feature = "logger"))] 186 | { 187 | vtx_shader = gl_util::shader_from_source( vtx_shader_src.as_ptr(), gl::VERTEX_SHADER, &mut error_message ).unwrap(); 188 | frag_shader = gl_util::shader_from_source( super::shaders::frag_shader_src.as_ptr(), gl::FRAGMENT_SHADER, &mut error_message ).unwrap(); 189 | unsafe{ 190 | shader_prog = gl_util::program_from_shaders(vtx_shader, frag_shader, &mut error_message ).unwrap(); 191 | } 192 | } 193 | 194 | #[cfg(feature = "logger")] 195 | { 196 | vtx_shader = match gl_util::shader_from_source( vtx_shader_src.as_ptr(), gl::VERTEX_SHADER, &mut error_message ) { 197 | Some( shader ) => shader, 198 | None => { super::show_error( error_message.as_ptr() ); 0 } 199 | }; 200 | unsafe{ 201 | super::util::read_file( "shader.glsl\0", &mut glbl_shader_code); 202 | frag_shader = match gl_util::shader_from_source( glbl_shader_code.as_ptr(), gl::FRAGMENT_SHADER, &mut error_message ) { 203 | Some( shader ) => shader, 204 | None => { super::show_error( error_message.as_ptr() ); 0 } 205 | }; 206 | } 207 | unsafe{ 208 | shader_prog = match gl_util::program_from_shaders(vtx_shader, frag_shader, &mut error_message ) { 209 | Some( prog ) => prog, 210 | None => { super::show_error( error_message.as_ptr() ); 0 } 211 | }; 212 | } 213 | } 214 | 215 | unsafe{ 216 | super::log!( "Build terrain!"); 217 | // Create the terrains by dropping some lumps and randomly aggregating points around them 218 | let mut rng_terrain : random::Rng = random::Rng{seed: core::num::Wrapping(7923129)}; 219 | let mut lumps : [[f32;4];50] = [[0f32;4];50]; 220 | let num_lumps = 50; 221 | 222 | let mut nl = 0; 223 | loop{ 224 | // do not put the lumps too close to the edges to avoid ugly discontinuities 225 | set_r3( lumps.get_unchecked_mut(nl), &mut rng_terrain,0.8f32,0.8f32,0.8f32, -0.1 ); 226 | nl += 1; 227 | if nl == num_lumps {break} 228 | } 229 | 230 | let mut i = 0; 231 | loop{ 232 | set_r3( spheres.get_unchecked_mut(0), &mut rng_terrain,1f32,1f32,1f32, 0.0 ); 233 | let x = spheres.get_unchecked_mut(0)[0]; 234 | let z = spheres.get_unchecked_mut(0)[2]; 235 | 236 | let mut charge = 0.0; 237 | nl = 0; 238 | loop{ 239 | let lmp = lumps.get(nl).unwrap(); 240 | let dist = (x-lmp[0])*(x-lmp[0]) + (z-lmp[2])*(z-lmp[2]); 241 | charge += lmp[1]*0.0001/dist; 242 | nl += 1; 243 | if nl == num_lumps { break;} 244 | } 245 | *src_terrain.get_unchecked_mut( r3_pos ) += charge; 246 | if *src_terrain.get_unchecked( r3_pos ) > 1.0 { 247 | *src_terrain.get_unchecked_mut( r3_pos ) = 1.0 248 | } 249 | i += 1; 250 | if i== 700_000 { break} 251 | } 252 | 253 | // Smooth the terrain once to make it less 'craggy' 254 | smooth( &mut src_terrain); 255 | } 256 | 257 | let mut vertex_buffer_id : gl::GLuint = 0; 258 | unsafe{ 259 | // Create the map texture 260 | gl::GenTextures( 1, &mut tex_buffer_id ); 261 | gl::ActiveTexture(gl::TEXTURE0); 262 | gl::BindTexture( gl::TEXTURE_2D, tex_buffer_id ); 263 | gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB, 512, 512, 0, gl::RGBA, gl::FLOAT, src_terrain.as_ptr() as *const CVoid); 264 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32); 265 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32); 266 | 267 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32 ); 268 | gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32 ); 269 | } 270 | } 271 | 272 | 273 | static mut delay_counter : i32 = 0; 274 | static mut play_pos : usize = 0; 275 | static mut camera_speed : f32 = 1.0; 276 | 277 | fn update_world( now: f32 ) { 278 | 279 | unsafe{ 280 | delay_counter = *SEQUENCE.get_unchecked( play_pos*2+0 ) as i32*60; 281 | let arg : u32 = ((*SEQUENCE.get_unchecked( play_pos*2+1 )) & 0x0fff ) as u32; 282 | let mode : u16 = (*SEQUENCE.get_unchecked( play_pos*2+1 )) & 0xf000; 283 | 284 | super::log!( "Camera", arg as f32, camera_mode as f32); 285 | if mode == MODE_CAM_PAN { 286 | setup_camera( arg, camera_mode as u8 ); 287 | } else if mode == MODE_CAM_SPEED { 288 | camera_speed = arg as f32; 289 | } else { 290 | sphere_delta = now; 291 | sphere_scale = arg as f32; 292 | } 293 | play_pos += 1; 294 | } 295 | } 296 | 297 | static mut cam_count : u32 = 1918; // (1753 0 ) 298 | 299 | fn setup_random_camera( ) { 300 | let seed : u32; 301 | unsafe{ 302 | cam_count += 1; 303 | setup_camera( cam_count, 0); 304 | camera_mode = 0; 305 | 306 | } 307 | } 308 | 309 | 310 | fn setup_camera( seed : u32, mode : u8) { 311 | unsafe{ super::log!( "Setup Camera: ", mode as f32, seed as f32 ); } 312 | 313 | let mut crng : random::Rng = random::Rng{seed: core::num::Wrapping(9231249+seed)}; 314 | unsafe{ super::log!( "Setup Camera: ", 2.0 ); } 315 | unsafe{ 316 | super::log!( "Setup Camera: ", 11.0 ); 317 | set_r3( &mut global_spheres[ CAMERA_POS_IDX ], &mut crng, 512f32, 512f32, 512f32, 0.0); 318 | global_spheres[ CAMERA_POS_IDX ][ 1 ] = (*src_terrain.get_unchecked( r3_pos ))*60.0-2.1+crng.next_f32()*5.0; 319 | super::log!( "Setup Camera: ", 12.0 ); 320 | set_r3( &mut global_spheres[ CAMERA_ROT_IDX ], &mut crng, FP_1_54, 3.15, FP_0_05, 0.5 ); 321 | set_r3( &mut camera_velocity, &mut crng, FP_0_20, FP_0_05, FP_0_20, 0.5); 322 | set_r3( &mut camera_rot_speed, &mut crng, 0.002, 0.001, 0.001, 0.5 ); 323 | } 324 | unsafe{ super::log!( "Setup Camera: ", 3.0 ); } 325 | 326 | } 327 | 328 | const MODE_CAM_PAN : u16 = 0x1000; 329 | const MODE_CAM_PIVOT : u16 = 0x3000; 330 | const MODE_CAM_SPEED : u16 = 0x4000; 331 | const MODE_SPHERE_SCALE : u16 = 0x5000; 332 | 333 | static SEQUENCE : &[u16] = &[ 334 | // 1200, MODE_CAM_PAN | 1612, 335 | // Slow pan in 336 | 28, MODE_CAM_PAN | 786 , 337 | // Quick camera flashes 338 | 2, MODE_CAM_PAN | 1223 , 339 | 2, MODE_CAM_PAN | 1239 , 340 | 2, MODE_CAM_PAN | 2157, // join slow upshot 341 | // Hold on up side wall 342 | 4, MODE_CAM_PAN | 945 , 343 | 344 | 345 | // Pan forward /// find better 346 | 4, MODE_CAM_PAN | 2290, // forward wtith accel 347 | 1, MODE_CAM_SPEED | 12 , 348 | 7, MODE_CAM_SPEED | 1 , 349 | 350 | // up again and release the spheres 351 | 1, MODE_CAM_PAN | 1849, 352 | 23, MODE_SPHERE_SCALE | 48, 353 | 354 | // lock down the spheres again 355 | 0, MODE_SPHERE_SCALE | 1, 356 | 3, MODE_CAM_PAN | 2102, // spin down 357 | 1, MODE_CAM_SPEED | 12 , 358 | 4, MODE_CAM_SPEED | 1 , 359 | 360 | 4, MODE_CAM_PAN | 2156, // dunk down 361 | 0, MODE_SPHERE_SCALE | 48, 362 | 22, MODE_CAM_PAN | 2118, //** 363 | 16, MODE_CAM_PAN | 1011, 364 | 365 | ]; 366 | 367 | pub fn frame( now : f32 ) -> () { 368 | set_sphere_positions(now); 369 | 370 | unsafe { 371 | if delay_counter <= 0 { 372 | update_world( now ); 373 | global_spheres[ CAMERA_CUT_INFO ][ 1 ] = 0f32; 374 | 375 | } 376 | delay_counter -= 1; 377 | global_spheres[ CAMERA_CUT_INFO ][ 1 ] += 1f32; 378 | } 379 | 380 | unsafe{ 381 | // let mut dst:x86::__m128 = core::arch::x86::_mm_load_ps(global_spheres[ CAMERA_ROT_IDX ].as_mut_ptr()); 382 | // let mut src:x86::__m128 = core::arch::x86::_mm_load_ps(camera_rot_speed.as_mut_ptr()); 383 | // dst = core::arch::x86::_mm_add_ps( dst, src); 384 | // core::arch::x86::_mm_store_ss( (&mut global_spheres[ CAMERA_ROT_IDX ]).as_mut_ptr(), dst ); 385 | global_spheres[ CAMERA_ROT_IDX ][ 0 ] += camera_rot_speed[ 0 ]*camera_speed; 386 | global_spheres[ CAMERA_ROT_IDX ][ 1 ] += camera_rot_speed[ 1 ]*camera_speed; 387 | global_spheres[ CAMERA_ROT_IDX ][ 2 ] += camera_rot_speed[ 2 ]*camera_speed; 388 | // dst = core::arch::x86::_mm_load_ps(global_spheres[ CAMERA_POS_IDX ].as_mut_ptr()); 389 | // src = core::arch::x86::_mm_load_ps(camera_velocity.as_mut_ptr()); 390 | // dst = core::arch::x86::_mm_add_ps( dst, src); 391 | // core::arch::x86::_mm_store_ss( (&mut global_spheres[ CAMERA_POS_IDX ]).as_mut_ptr(), dst ); 392 | global_spheres[ CAMERA_POS_IDX ][ 0 ] += camera_velocity[ 0 ]*camera_speed; 393 | global_spheres[ CAMERA_POS_IDX ][ 1 ] += camera_velocity[ 1 ]*camera_speed; 394 | global_spheres[ CAMERA_POS_IDX ][ 2 ] += camera_velocity[ 2 ]*camera_speed; 395 | 396 | global_spheres[ CAMERA_CUT_INFO ][ 0 ] = delay_counter as f32; 397 | global_spheres[ CAMERA_CUT_INFO ][ 2 ] = now; 398 | } 399 | 400 | unsafe{ 401 | gl::UseProgram(shader_prog); 402 | let shperes_loc : i32 = gl::GetUniformLocation(shader_prog, "sp\0".as_ptr()); 403 | gl::Uniform4fv(shperes_loc, (num_spheres+sphere_extras) as i32 * 2, transmute::<_,*const gl::GLfloat>( global_spheres.as_ptr() ) ); 404 | gl::Recti( -1, -1, 1, 1 ); 405 | } 406 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(asm)] 2 | #![no_main] 3 | #![no_std] 4 | #![windows_subsystem = "windows"] 5 | #![feature(core_intrinsics)] 6 | 7 | #[cfg(windows)] extern crate winapi; 8 | 9 | mod shaders; 10 | mod math_util; 11 | mod gl; 12 | pub mod gl_util; 13 | pub mod util; 14 | mod intro; 15 | mod music; 16 | mod random; 17 | 18 | use core::mem::MaybeUninit; 19 | use core::panic::PanicInfo; 20 | 21 | 22 | use winapi::um::wingdi::{ 23 | ChoosePixelFormat, 24 | SwapBuffers, 25 | wglMakeCurrent, 26 | wglCreateContext, 27 | SetPixelFormat, 28 | 29 | DEVMODEA, 30 | PFD_TYPE_RGBA, 31 | PFD_DOUBLEBUFFER, 32 | PFD_SUPPORT_OPENGL, 33 | PFD_DRAW_TO_WINDOW, 34 | PIXELFORMATDESCRIPTOR 35 | }; 36 | 37 | use winapi::shared::minwindef::{ 38 | HINSTANCE, 39 | LRESULT, 40 | LPARAM, 41 | LPVOID, 42 | WPARAM, 43 | UINT, 44 | }; 45 | 46 | use winapi::shared::windef::{ 47 | HDC, 48 | HGLRC, 49 | HWND, 50 | HMENU, 51 | HICON, 52 | HBRUSH, 53 | }; 54 | 55 | use winapi::um::libloaderapi::GetModuleHandleA; 56 | 57 | use winapi::um::winuser::{ 58 | CreateWindowExA, 59 | DefWindowProcA, 60 | DispatchMessageA, 61 | GetDC, 62 | PostQuitMessage, 63 | RegisterClassA, 64 | TranslateMessage, 65 | PeekMessageA, 66 | MessageBoxA, 67 | 68 | MB_ICONERROR, 69 | MSG, 70 | WNDCLASSA, 71 | CS_OWNDC, 72 | CS_HREDRAW, 73 | CS_VREDRAW, 74 | CW_USEDEFAULT, 75 | PM_REMOVE, 76 | WS_OVERLAPPEDWINDOW, 77 | WS_MAXIMIZE, 78 | WS_POPUP, 79 | WS_VISIBLE, 80 | }; 81 | 82 | 83 | #[cfg(not(feature = "logger"))] 84 | pub unsafe extern "system" fn window_proc(hwnd: HWND, 85 | msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT { 86 | 87 | match msg { 88 | winapi::um::winuser::WM_DESTROY => { 89 | PostQuitMessage(0); 90 | } 91 | _ => { return DefWindowProcA(hwnd, msg, w_param, l_param); } 92 | } 93 | return 0; 94 | } 95 | 96 | #[cfg(feature = "logger")] 97 | pub unsafe extern "system" fn window_proc(hwnd: HWND, 98 | msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT { 99 | 100 | match msg { 101 | winapi::um::winuser::WM_DESTROY => { 102 | PostQuitMessage(0); 103 | }, 104 | winapi::um::winuser::WM_MOUSEMOVE => { 105 | let x_pos = ( ( l_param as u32 ) & 0x0000ffff) as i32; 106 | let y_pos = ((( l_param as u32 ) & 0xffff0000)>>16) as i32; 107 | let ctrl : bool = ( w_param & winapi::um::winuser::MK_CONTROL ) != 0; 108 | intro::set_pos(x_pos, y_pos, ctrl); 109 | }, 110 | winapi::um::winuser::WM_LBUTTONDOWN => { 111 | let x_pos = ( ( l_param as u32 ) & 0x0000ffff) as i32; 112 | let y_pos = ((( l_param as u32 ) & 0xffff0000)>>16) as i32; 113 | intro::lbutton_down(x_pos,y_pos); 114 | }, 115 | winapi::um::winuser::WM_LBUTTONUP => { 116 | intro::lbutton_up(); 117 | } 118 | winapi::um::winuser::WM_RBUTTONDOWN => { 119 | let x_pos = ( ( l_param as u32 ) & 0x0000ffff) as i32; 120 | let y_pos = ((( l_param as u32 ) & 0xffff0000)>>16) as i32; 121 | intro::rbutton_down(x_pos,y_pos); 122 | }, 123 | winapi::um::winuser::WM_RBUTTONUP => { 124 | intro::rbutton_up(); 125 | } 126 | _ => { return DefWindowProcA(hwnd, msg, w_param, l_param); } 127 | } 128 | return 0; 129 | } 130 | 131 | #[cfg(feature = "logger")] 132 | fn show_error( message : *const i8 ) { 133 | unsafe{ 134 | MessageBoxA(0 as HWND, message, "Window::create\0".as_ptr() as *const i8, MB_ICONERROR); 135 | } 136 | } 137 | 138 | fn create_window( ) -> ( HWND, HDC ) { 139 | unsafe { 140 | let h_wnd : HWND; 141 | 142 | #[cfg(feature = "fullscreen")] 143 | { 144 | let mut devMode : DEVMODEA = core::mem::zeroed(); 145 | devMode.dmSize = core::mem::size_of::() as u16; 146 | devMode.dmFields = winapi::um::wingdi::DM_BITSPERPEL | winapi::um::wingdi::DM_PELSWIDTH | winapi::um::wingdi::DM_PELSHEIGHT; 147 | devMode.dmBitsPerPel = 32; 148 | devMode.dmPelsWidth = 1920; 149 | devMode.dmPelsHeight = 1080; 150 | if winapi::um::winuser::ChangeDisplaySettingsA(&mut devMode, winapi::um::winuser::CDS_FULLSCREEN)!= winapi::um::winuser::DISP_CHANGE_SUCCESSFUL { 151 | return ( 0 as HWND, 0 as HDC ) ; 152 | } 153 | winapi::um::winuser::ShowCursor( 0 ); 154 | 155 | h_wnd = CreateWindowExA( 156 | 0, 157 | "static\0".as_ptr() as *const i8, // class we registered. 158 | "GLWIN\0".as_ptr() as *const i8, // title 159 | WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, // size and position 160 | 0 as HWND, // hWndParent 161 | 0 as HMENU, // hMenu 162 | 0 as HINSTANCE, // hInstance 163 | 0 as LPVOID ); // lpParam 164 | } 165 | 166 | #[cfg(not(feature = "fullscreen"))] 167 | { 168 | let hinstance = GetModuleHandleA( 0 as *const i8 ); 169 | let mut wnd_class : WNDCLASSA = core::mem::zeroed(); 170 | wnd_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; 171 | wnd_class.lpfnWndProc = Some( window_proc ); 172 | wnd_class.hInstance = hinstance; // The instance handle for our application which we can retrieve by calling GetModuleHandleW. 173 | wnd_class.lpszClassName = "MyClass\0".as_ptr() as *const i8; 174 | RegisterClassA( &wnd_class ); 175 | 176 | h_wnd = CreateWindowExA( 177 | 0, 178 | //WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // dwExStyle 179 | "MyClass\0".as_ptr() as *const i8, // class we registered. 180 | "GLWIN\0".as_ptr() as *const i8, // title 181 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, // dwStyle 182 | CW_USEDEFAULT, CW_USEDEFAULT, 1920, 1080, // size and position 183 | 0 as HWND, // hWndParent 184 | 0 as HMENU, // hMenu 185 | hinstance, // hInstance 186 | 0 as LPVOID ); // lpParam 187 | } 188 | let h_dc : HDC = GetDC(h_wnd); // Device Context 189 | 190 | let mut pfd : PIXELFORMATDESCRIPTOR = core::mem::zeroed(); 191 | pfd.nSize = core::mem::size_of::() as u16; 192 | pfd.nVersion = 1; 193 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 194 | pfd.iPixelType = PFD_TYPE_RGBA; 195 | pfd.cColorBits = 32; 196 | pfd.cAlphaBits = 8; 197 | pfd.cDepthBits = 32; 198 | 199 | #[cfg(feature = "logger")] 200 | { 201 | let pf_id : i32 = ChoosePixelFormat(h_dc, &pfd ); 202 | if pf_id == 0 { 203 | show_error( "ChoosePixelFormat() failed.\0".as_ptr() as *const i8); 204 | return ( 0 as HWND, h_dc ) ; 205 | } 206 | 207 | if SetPixelFormat(h_dc, pf_id, &pfd) == 0 { 208 | show_error( "SetPixelFormat() failed.\0".as_ptr() as *const i8); 209 | return ( 0 as HWND, h_dc ) ; 210 | } 211 | 212 | let gl_context : HGLRC = wglCreateContext(h_dc); // Rendering Contex 213 | if gl_context == 0 as HGLRC { 214 | show_error( "wglCreateContext() failed.\0".as_ptr() as *const i8 ); 215 | return ( 0 as HWND, h_dc ) ; 216 | } 217 | 218 | if wglMakeCurrent(h_dc, gl_context) == 0 { 219 | show_error( "wglMakeCurrent() failed.\0".as_ptr() as *const i8); 220 | return ( 0 as HWND, h_dc ) ; 221 | } 222 | } 223 | 224 | #[cfg(not(feature = "logger"))] 225 | { 226 | let pf_id : i32 = ChoosePixelFormat(h_dc, &pfd ); 227 | SetPixelFormat(h_dc, pf_id, &pfd); 228 | let gl_context : HGLRC = wglCreateContext(h_dc); // Rendering Context 229 | wglMakeCurrent(h_dc, gl_context); 230 | } 231 | 232 | 233 | // make the system font the device context's selected font 234 | winapi::um::wingdi::SelectObject (h_dc, winapi::um::wingdi::GetStockObject (winapi::um::wingdi::SYSTEM_FONT as i32)); 235 | 236 | // create the bitmap display lists 237 | winapi::um::wingdi::wglUseFontBitmapsA (h_dc, 0, 255, 1000); 238 | 239 | gl::init(); 240 | gl::wglSwapIntervalEXT(1); 241 | ( h_wnd, h_dc ) 242 | } 243 | } 244 | 245 | // Create message handling function with which to link to hook window to Windows messaging system 246 | // More info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).aspx 247 | #[cfg(feature = "logger")] 248 | fn handle_message( _window : HWND ) -> bool { 249 | unsafe { 250 | let mut msg : MSG = MaybeUninit::uninit().assume_init(); 251 | loop{ 252 | if PeekMessageA( &mut msg,0 as HWND,0,0,PM_REMOVE) == 0 { 253 | return true; 254 | } 255 | if msg.message == winapi::um::winuser::WM_QUIT { 256 | return false; 257 | } 258 | TranslateMessage( &msg ); 259 | DispatchMessageA( &msg ); 260 | } 261 | } 262 | } 263 | 264 | 265 | #[panic_handler] 266 | #[no_mangle] 267 | pub extern fn panic( _info: &PanicInfo ) -> ! { loop {} } 268 | 269 | #[no_mangle] 270 | pub unsafe extern fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 { 271 | let mut i = 0; 272 | while i < n { 273 | *((dest as usize + i) as *mut u8) = c as u8; 274 | i += 1; 275 | } 276 | dest 277 | } 278 | 279 | #[no_mangle] 280 | pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { 281 | let mut i = 0; 282 | while i < n { 283 | *((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8); 284 | i += 1; 285 | } 286 | dest 287 | } 288 | 289 | static waveFormat : winapi::shared::mmreg::WAVEFORMATEX = winapi::shared::mmreg::WAVEFORMATEX{ 290 | wFormatTag : winapi::shared::mmreg::WAVE_FORMAT_IEEE_FLOAT, 291 | nChannels : 1, 292 | nSamplesPerSec : 44100, 293 | nAvgBytesPerSec : 44100*4, 294 | nBlockAlign : 4, 295 | wBitsPerSample: 32, 296 | cbSize:0 297 | }; 298 | 299 | static mut waveHeader : winapi::um::mmsystem::WAVEHDR = winapi::um::mmsystem::WAVEHDR{ 300 | lpData: 0 as *mut i8, 301 | dwBufferLength: 44100*4*120, 302 | dwBytesRecorded: 0, 303 | dwUser: 0, 304 | dwFlags: 0, 305 | dwLoops: 0, 306 | lpNext: 0 as *mut winapi::um::mmsystem::WAVEHDR, 307 | reserved: 0, 308 | }; 309 | 310 | static mut music_data : [f32;44100*120] = [ 0.0;44100*120]; 311 | #[no_mangle] 312 | pub extern "system" fn mainCRTStartup() { 313 | let ( window, hdc ) = create_window( ); 314 | 315 | unsafe{ log!("Prepare\n"); }; 316 | intro::prepare(); 317 | 318 | let mut time : f32 = 0.0; 319 | 320 | unsafe{ 321 | music::make_music( &mut music_data ); 322 | waveHeader.lpData = music_data.as_mut_ptr() as *mut i8; 323 | let mut hWaveOut : winapi::um::mmsystem::HWAVEOUT = 0 as winapi::um::mmsystem::HWAVEOUT; 324 | winapi::um::mmeapi::waveOutOpen( &mut hWaveOut, winapi::um::mmsystem::WAVE_MAPPER, &waveFormat, 0, 0, winapi::um::mmsystem::CALLBACK_NULL); 325 | winapi::um::mmeapi::waveOutPrepareHeader(hWaveOut, &mut waveHeader, core::mem::size_of::() as u32 ); 326 | winapi::um::mmeapi::waveOutWrite(hWaveOut, &mut waveHeader, core::mem::size_of::() as u32 ); 327 | } 328 | 329 | unsafe{ log!("Entering loop\n"); }; 330 | loop { 331 | #[cfg(feature = "logger")] 332 | { 333 | if !handle_message( window ) { 334 | break; 335 | } 336 | } 337 | 338 | unsafe{ 339 | if winapi::um::winuser::GetAsyncKeyState(winapi::um::winuser::VK_ESCAPE) != 0 { 340 | break; 341 | } 342 | } 343 | 344 | intro::frame( time ); 345 | unsafe{ 346 | gl::UseProgram(0); 347 | gl::ListBase (1000); 348 | // now draw the characters in a string 349 | let mut xoffset = time-4.0; 350 | if xoffset < 0.0 { 351 | xoffset = -xoffset; 352 | } 353 | if xoffset < 1.0 { 354 | xoffset = 1.0; 355 | } 356 | gl::RasterPos2f( 0.65+ xoffset*0.1, 0.1 ); 357 | gl::CallLists (15, gl::UNSIGNED_BYTE, "Code | janiorca\0".as_ptr() as *const gl::CVoid ); 358 | } 359 | 360 | unsafe{ SwapBuffers(hdc); } 361 | time += 1.0 / 60.0f32; 362 | #[cfg(not(feature = "logger"))] 363 | if time > 120.0 { 364 | break; 365 | } 366 | } 367 | 368 | unsafe{ 369 | // Tying to exit normally seems to crash after certain APIs functions have been called. ( Like ChoosePixelFormat ) 370 | winapi::um::processthreadsapi::ExitProcess(0); 371 | } 372 | } 373 | 374 | // Compiling with no_std seems to require the following symbol to be set if there is any floating point code anywhere in the code 375 | #[no_mangle] 376 | pub static _fltused : i32 = 1; 377 | -------------------------------------------------------------------------------- /src/math_util.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | 3 | #[inline(always)] 4 | pub fn sin(a: f32) -> f32 { 5 | 6 | let mut res: f32 = unsafe { mem::uninitialized() }; 7 | 8 | unsafe { asm!( 9 | r##" 10 | flds $1; 11 | fsin; 12 | fstps $0; 13 | "## 14 | : "=*m"(&mut res as *mut f32) 15 | : "*m"(&a as *const f32) 16 | ) }; 17 | 18 | res 19 | } 20 | 21 | #[inline(always)] 22 | pub fn cos(a: f32) -> f32 { 23 | 24 | let mut res: f32 = unsafe { mem::uninitialized() }; 25 | 26 | unsafe { asm!( 27 | r##" 28 | flds $1; 29 | fcos; 30 | fstps $0; 31 | "## 32 | : "=*m"(&mut res as *mut f32) 33 | : "*m"(&a as *const f32) 34 | ) }; 35 | 36 | res 37 | } 38 | -------------------------------------------------------------------------------- /src/music.rs: -------------------------------------------------------------------------------- 1 | use super::random; 2 | 3 | static mut sounds : [[f32;44100*9];7] = [[0.0;44100*9];7]; 4 | static frequencies : [ f32; 7] = [ 5 | 349.0, //F4 6 | 415.0, //Ab4 1.189 7 | 523.0, //C5 1.26 8 | 554.0, //Db5 1.059 9 | 622.0, //Eb5 1.122 10 | 698.0, //F5 1.22 11 | 831.0]; //Ab5 12 | 13 | fn play( dst: &mut [f32;44100*120], dst_offset : usize, signal : &[f32;44100*9], sample_duration : f32 ) { 14 | let mut dst_pos = 0; 15 | let mut position : f32 = 0.0; 16 | unsafe{ 17 | loop{ 18 | let src_val = signal.get_unchecked(dst_pos); 19 | let in_pos = position/4.5-2f32; 20 | let val = (in_pos*in_pos)*position/4.5; 21 | *dst.get_unchecked_mut( dst_pos + dst_offset) += src_val*val; 22 | 23 | position += sample_duration; 24 | dst_pos += 1; 25 | if dst_pos == 44100*9 { 26 | return; 27 | } 28 | } 29 | } 30 | } 31 | 32 | pub fn make_music( music: &mut [f32;44100*120]) { 33 | let mut vrng = random::Rng::new_unseeded(); 34 | 35 | unsafe{ super::log!( "Make instruments!"); }; 36 | 37 | let mut i = 0; 38 | loop{ 39 | let mut scale = 1.0; 40 | // # Could combine into a single loop that doubles the scales when loop % 11 == 0. Possibly slightly shorter 41 | unsafe{ 42 | loop{ 43 | let mut d = 0; 44 | loop{ 45 | let frequency : f32 = frequencies.get_unchecked(i)/scale+6.0*vrng.next_f32(); 46 | let mut position : f32 = 0.0; 47 | let mut sample_no = 0; 48 | loop { 49 | let sample_duration : f32 = frequency / 44100.0f32; 50 | position = position + sample_duration; 51 | if position > 0.5 { 52 | position -= 1.0f32; 53 | } 54 | let val = core::intrinsics::fabsf32(position)*4f32-1.0f32; 55 | *sounds.get_unchecked_mut(i).get_unchecked_mut(sample_no) += val/55.0f32; 56 | sample_no += 1; 57 | if sample_no == 44100*9 { 58 | break; 59 | } 60 | } 61 | d += 1; 62 | if d == 11 { 63 | break; 64 | } 65 | } 66 | scale *= 2.0f32; 67 | if scale >= 32.0 { 68 | break; 69 | } 70 | } 71 | } 72 | i += 1; 73 | if i == 7 { 74 | break; 75 | } 76 | } 77 | 78 | unsafe{ 79 | let mut mrng : random::Rng = random::Rng{seed: core::num::Wrapping(1161249)}; 80 | 81 | let mut dst : usize = 0; 82 | let mut s = 0; 83 | loop { 84 | let mut i = 0; 85 | loop{ 86 | let nt = mrng.next_f32(); 87 | if nt > 0.9 { 88 | play( music, dst, &sounds[i], 1.0 / 44100.0 ); 89 | } 90 | i += 1; 91 | if i == 7 { 92 | break; 93 | } 94 | } 95 | dst += 44100; 96 | s += 1; 97 | if s == 110 { 98 | break; 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/random.rs: -------------------------------------------------------------------------------- 1 | use core::mem::transmute; 2 | use core::num::Wrapping; 3 | 4 | pub struct Rng { 5 | pub seed: Wrapping, 6 | } 7 | 8 | impl Rng { 9 | pub fn new_unseeded() -> Rng { 10 | Rng { 11 | seed: Wrapping(0x66126c8d), 12 | } 13 | } 14 | 15 | pub fn next_u32(&mut self) -> u32 { 16 | self.seed = self.seed*Wrapping(214013) + Wrapping(2531011); 17 | self.seed.0 18 | } 19 | 20 | pub fn next_f32(&mut self) -> f32 { 21 | const UPPER_MASK: u32 = 0x3F800000; 22 | const LOWER_MASK: u32 = 0x7FFFFF; 23 | let tmp = UPPER_MASK | (self.next_u32() & LOWER_MASK); 24 | let result: f32 = unsafe { transmute(tmp) }; 25 | result - 1.0 26 | } 27 | } -------------------------------------------------------------------------------- /src/shader_code.h: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in vec4 v;out vec4 f;uniform float e;uniform vec4 d[160];const int k=80;const vec3 i=normalize(vec3(1.,1.1,1.));const float z=99999.;float t(float z,vec3 v,vec3 e,vec3 i,float f,int d,out vec3 x,out vec3 o,out int y){vec3 w=i-e;float k=dot(w,v);if(k<0.)return z;else{float p=length(w),s=p*p-k*k;if(s>f)return z;else{float t=sqrt(f-s),c=k-t;if(cf)return false;else return true;}}float t(vec3 i,vec3 v,float f,out vec3 e,out vec3 x){if(i.y>=0.)return z;float k=(f-v.y)/i.y;x=vec3(0.,1.f,0.f);e=v+i*k;return k;}float t(float i,vec3 v,vec3 f){float e=(1.-i)/(1.+i);e*=e;float z=-dot(v,f),k=1.-z,o=e+(1.-e)*k*k*k*k*k;return o;}const vec3 x=vec3(5e-06,1.5e-05,.00027)*15.,o=vec3(.00015,.00015,.00027)*15.;vec3 t(float e){return exp(-e*(x+o));}vec3 t(float i,float v){vec3 k=4.*vec3(27.,4.,1.)/pow(i,4.);return k;}vec3 p(float i,float v){float k=.0003/16.*3.14159*(1.+v*v);vec3 f=vec3(1./(x.x+o.x)*(1.-exp(-i*o.x)),1./(x.y+o.y)*(1.-exp(-i*o.y)),1./(x.z+o.z)*(1.-exp(-i*o.z)));float e=.476;vec3 z=vec3(.002,.0008,.0002)*(1.-e)*(1.-e)/(12.5664*pow(1.+e*e-2.*e*v,1.5));float p=20./(x.x+o.x)*(1.-exp(-i*o.x));return k*f+z*p;}void main(){vec2 x=vec2(.5,.5);float o=e*2.,c=(30.*o-45.*cos(o)+cos(3.*o)-9.*sin(2.*o))/96.;c+=e*.1;vec2 w=v.xy/1200.,s=2.*(w-x);vec3 r=vec3(s,-39.);float y=c/3.+e*.2;mat3 n=mat3(cos(y),0,-sin(y),0,1,0,sin(y),0,cos(y));vec3 u=n*vec3(0,0,-40.),b=normalize(n*r-u);float m=1.;vec3 l=vec3(0,0,0);for(int a=2;a>0;a--){vec3 h,g,q,Z;float Y,X=0.;int W=-1;float V=z,U=0.;V=t(b,u,-18.,q,g);if(V<=z){Y=1.77;Z=vec3(.05,.05,.05);if((int(q.x/5.)+int(q.z/5.)&1)==1){float T=q.x*q.x+q.z*q.z;Z=mix(Z,vec3(.59,.6,.5),smoothstep(8000.,4000.,T));}}else Z=vec3(1.,1.,0.);for(int T=0;T=0)Z=d[W*2+1].xyz,Y=d[W*2+1].w,X=t(Y,g,b),h=reflect(b,g);vec3 T=vec3(0,0,0);if(V>=z){T+=p(V,dot(i,b));l+=T*m;break;}for(int S=0;S<1;S++){bool R=false;for(int Q=0;Qy)return false;else{float t=sqrt(y-o);i=s-t;return true;}}}float t(vec2 f,out float v){vec4 s=texture(terrain,f/512.);v=s.y;return s.x*60.-12.1;}void t(vec3 f,vec3 y,out float v,out float i){vec3 s=(vec3(0.)-f)/y,o=(vec3(512.)-f)/y;v=max(min(s.x,o.x),min(s.z,o.z));i=min(max(s.x,o.x),max(s.z,o.z));}bool t(vec3 v,vec3 f,out float i,out vec3 y,out vec3 o,out float e,out float s){float z,n;t(v,f,z,n);if(n=m.y));i=dot(m,k);float g=v.y+f.y*i;c=c+d*k;m=m+r*k;if(u>g)return y=vec3(.2,.071,.01)+step(27,u)*vec3(.1,-.06,0),o=vec3(0,1.,0),i=(u-v.y)/f.y,true;u=t(c,s);if(u>g){e=1.5;y=vec3(.2,.2,.2)+step(40,u)*vec3(0.,-.03,-.1);if(s==1.){float a=25.31;if(g0;u--){vec3 p,g,a,b;float w,l=0.,C=e;for(int F=0;F=e||u==1){c+=n(C,dot(s,r))*k;break;}bool Z=t(a,s,F,D,q,w,h);if(!Z){for(int Y=0;Yz)return false;else{float t=sqrt(z-o);i=s-t;return true;}}}float t(vec2 f,out float v){vec4 s=texture(terrain,f/512.);v=s.y;return s.x*60.-12.1;}void t(vec3 f,vec3 e,out float v,out float i){vec3 s=(vec3(0.)-f)/e,o=(vec3(512.)-f)/e;v=max(min(s.x,o.x),min(s.z,o.z));i=min(max(s.x,o.x),max(s.z,o.z));}bool t(vec3 v,vec3 f,out float i,out vec3 e,out vec3 o,out float z,out float s){float y,n;t(v,f,y,n);if(n=m.y));i=dot(m,k);float g=v.y+f.y*i;c=c+d*k;m=m+r*k;if(u>g)return e=vec3(.2,.071,.01)+step(27,u)*vec3(.1,-.06,0),z=1.2,o=vec3(0,1.,0),i=(u-v.y)/f.y,true;u=t(c,s);if(u>g){z=1.5;e=vec3(.2,.2,.2)+step(40,u)*vec3(0.,-.03,-.1);if(s==1.){float a=25.31;if(g0;u--){vec3 p,g,a,b;float w,l=0.,C=e;for(int F=0;F=e){c+=n(C,dot(s,r))*k;break;}bool Z=t(a,s,F,D,q,w,h);if(!Z){for(int Y=0;Yy)return false;else{float t=sqrt(y-o);i=m-t;return true;}}}float t(vec2 f,out float v){vec4 s=texture(terrain,f/512.);v=s.y;return s.x*60.-12.1;}void t(vec3 f,vec3 y,out float v,out float i){vec3 s=(vec3(0.)-f)/y,o=(vec3(512.)-f)/y;v=max(min(s.x,o.x),min(s.z,o.z));i=min(max(s.x,o.x),max(s.z,o.z));}bool t(vec3 v,vec3 s,out float f,out vec3 i,out vec3 y,out float o,out float m){float x,e;t(v,s,x,e);if(e=n.y));f=dot(n,k);float g=v.y+s.y*f;c=c+d*k;n=n+r*k;if(u>g)return i=vec3(.2,.071,.01)+step(27,u)*vec3(.1,-.06,0),o=1.2,y=vec3(0,1.,0),f=(u-v.y)/s.y,true;u=t(c,m);if(u>g){o=1.5;i=vec3(.2,.2,.2)+step(40,u)*vec3(0.,-.03,-.1);if(m==1.){float a=25.31;if(g0;p--){vec3 k,g,a,l;float w,b=0.,C=e;for(int h=0;h=e){u+=n(C,dot(s,z))*r;break;}bool Z=t(a,s,h,F,D,w,q);if(!Z){for(int Y=0;Y { crate::util::log0($text); }; 23 | ($text:expr, $val:expr) => { crate::util::log1($text,$val); }; 24 | ($text:expr, $val1:expr, $val2:expr) => { crate::util::log2($text,$val1,$val2); }; 25 | ($text:expr, $val1:expr, $val2:expr, $val3:expr) => { crate::util::log3($text,$val1,$val2,$val3); }; 26 | } 27 | 28 | #[cfg(not(feature = "logger"))] 29 | #[macro_export] 30 | macro_rules! log { 31 | ($text:expr) => { }; 32 | ($text:expr, $val:expr) => {}; 33 | ($text:expr, $val1:expr, $val2:expr) => {}; 34 | ($text:expr, $val1:expr, $val2:expr, $val3:expr) => {}; 35 | } 36 | 37 | #[cfg(feature = "logger")] 38 | pub unsafe fn log0( message : &str ) { 39 | let name = "dbg_out.txt\0"; 40 | let mut out = 0; 41 | 42 | let hFile = CreateFileA( name.as_ptr() as *const i8, FILE_APPEND_DATA, 0, 43 | 0 as *mut winapi::um::minwinbase::SECURITY_ATTRIBUTES, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 44 | 0 as *mut winapi::ctypes::c_void ); 45 | WriteFile( hFile, message.as_ptr() as *const winapi::ctypes::c_void, message.len() as u32, &mut out, 46 | 0 as *mut winapi::um::minwinbase::OVERLAPPED ); 47 | CloseHandle( hFile ); 48 | } 49 | 50 | #[cfg(feature = "logger")] 51 | pub fn get_c_string_length( buffer: &[u8]) -> usize { 52 | let mut buffer_text_len = 0; 53 | while buffer_text_len < buffer.len() { 54 | if buffer[buffer_text_len] == 0 { 55 | break; 56 | } 57 | buffer_text_len += 1 58 | } 59 | return buffer_text_len; 60 | } 61 | 62 | #[cfg(feature = "logger")] 63 | pub fn f32_to_text( dest: &mut[u8], value: f32, comma: bool ) -> usize { 64 | let int_part = value as u32; 65 | let frac_part = ((value - int_part as f32)*10000f32 ) as u32; 66 | unsafe{ winapi::um::winuser::wsprintfA( dest.as_mut_ptr() as * mut i8, "%d.%.4d\0".as_ptr() as * const i8, int_part, frac_part); } 67 | if comma { 68 | let length = get_c_string_length(dest); 69 | dest[ length ] = ',' as u8; 70 | dest[ length+1 ] = ' ' as u8; 71 | return length+2; 72 | } 73 | return get_c_string_length( &dest ); 74 | } 75 | 76 | #[cfg(feature = "logger")] 77 | pub unsafe fn log1( message : &str, value: f32 ) { 78 | let mut buffer : [ u8; 256 ] = [ 0;256 ]; 79 | let mut length : usize = 0; 80 | length += f32_to_text( &mut buffer, value, false ); 81 | buffer[ length ] = '\n' as u8; 82 | let mut buffer_text_len = get_c_string_length(&buffer); 83 | log0( core::str::from_utf8_unchecked(&buffer[ 0 .. buffer_text_len ])); 84 | } 85 | 86 | #[cfg(feature = "logger")] 87 | pub unsafe fn log2( message : &str, value1: f32, value2: f32 ) { 88 | let mut buffer : [ u8; 256 ] = [ 0;256 ]; 89 | let mut length : usize = 0; 90 | length += f32_to_text( &mut buffer, value1, true ); 91 | length += f32_to_text( &mut buffer[length..], value2, false ); 92 | buffer[ length ] = '\n' as u8; 93 | let mut buffer_text_len = get_c_string_length(&buffer); 94 | log0( core::str::from_utf8_unchecked(&buffer[ 0 .. buffer_text_len ])); 95 | } 96 | 97 | #[cfg(feature = "logger")] 98 | pub unsafe fn log3( message : &str, value1: f32, value2: f32, value3: f32 ) { 99 | let mut buffer : [ u8; 256 ] = [ 0;256 ]; 100 | let mut length : usize = 0; 101 | length += f32_to_text( &mut buffer, value1, true ); 102 | length += f32_to_text( &mut buffer[length..], value2, true ); 103 | length += f32_to_text( &mut buffer[length..], value3, false ); 104 | buffer[ length ] = '\n' as u8; 105 | let mut buffer_text_len = get_c_string_length(&buffer); 106 | log0( core::str::from_utf8_unchecked(&buffer[ 0 .. buffer_text_len ])); 107 | } 108 | 109 | #[cfg(feature = "logger")] 110 | pub unsafe fn read_file( file_name : &str, dst : &mut [u8] ) { 111 | let name = "dbg_out.txt\0"; 112 | let mut out = 0; 113 | 114 | log!( "Creating file for reading\n"); 115 | let hFile = CreateFileA( file_name.as_ptr() as *const i8, GENERIC_READ, 0, 116 | 0 as *mut winapi::um::minwinbase::SECURITY_ATTRIBUTES, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 117 | 0 as *mut winapi::ctypes::c_void ); 118 | log!( "Reading...\n"); 119 | ReadFile( hFile, dst.as_mut_ptr() as *mut winapi::ctypes::c_void, dst.len() as u32, &mut out, 120 | 0 as *mut winapi::um::minwinbase::OVERLAPPED ); 121 | log!( "Close handle...\n"); 122 | CloseHandle( hFile ); 123 | } 124 | -------------------------------------------------------------------------------- /xargo.toml: -------------------------------------------------------------------------------- 1 | # [dependencies] 2 | # std = {default-features=false,features=["panic_immediate_abort"]} 3 | 4 | [dependencies] 5 | std = {features=["panic_immediate_abort"]} --------------------------------------------------------------------------------