commit beea0baeed55b60ba6b0790877f440617cf6e460 Author: lewa_j Date: Wed Aug 7 04:06:06 2019 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4de9ea9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Binaries +*.o +*.so +*.a +*.exe +*.dll + +# Qt Creator +*.user* +*.config +*.creator +*.includes +*.files + +bin +obj +obj_lin + +*.mdl +*.vtx +*.vvd diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f2b0c7 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +=nenuzhno engine= + +This repository contains the old discontinued codebase of my training engine/framework. I'm currently working on a new iteration of an engine, with better architecture (that's what i'm counting on). + +I put it here so as not to lose by accident. + +Writen in C++. Uses OpenGL/ES 2. + +==Dependencies== +MinGW-W64 +GLFW 3.3 +GLM 0.9.9.0 +GLEW 2.0 + +==demos== +StarFleet - space battles "game". With basic fighter AI, but withous gameplay +tpsGame - atempt to make real game. With bullet phisics, text map format, even menu +VolLight - Volumetric lighting +gravity - 2D simulation "game" +skinning - loads mdl files (Source engine) and renders some animation +SSR - kinda screen space reflections +path_tracing - uses glsl shader for ray trace +blur-test - atempt to do bokeh +cube - just wireframe cube +ComprTex - demonstrates some compressed texture formats + diff --git a/assets/base/fonts/sansation.fnt b/assets/base/fonts/sansation.fnt new file mode 100644 index 0000000..0978116 Binary files /dev/null and b/assets/base/fonts/sansation.fnt differ diff --git a/assets/base/fonts/sansation_0.dds b/assets/base/fonts/sansation_0.dds new file mode 100644 index 0000000..2ce4c72 Binary files /dev/null and b/assets/base/fonts/sansation_0.dds differ diff --git a/assets/base/models/bridge1.nmf b/assets/base/models/bridge1.nmf new file mode 100644 index 0000000..b4bca43 Binary files /dev/null and b/assets/base/models/bridge1.nmf differ diff --git a/assets/base/models/cube.mesh b/assets/base/models/cube.mesh new file mode 100644 index 0000000..1abe733 Binary files /dev/null and b/assets/base/models/cube.mesh differ diff --git a/assets/base/models/cube.nmf b/assets/base/models/cube.nmf new file mode 100644 index 0000000..90ad235 Binary files /dev/null and b/assets/base/models/cube.nmf differ diff --git a/assets/base/shaders/bokeh.fs b/assets/base/shaders/bokeh.fs new file mode 100644 index 0000000..eed0e5f --- /dev/null +++ b/assets/base/shaders/bokeh.fs @@ -0,0 +1,12 @@ +#version 100 +precision highp float; + +varying vec4 v_col; +uniform sampler2D u_coreTex; + +void main() +{ + //vec4 col = texture2D(u_coreTex, gl_PointCoord.xy); + vec4 col = vec4(length(gl_PointCoord.xy-0.5)<0.5); + gl_FragColor = col*v_col; +} diff --git a/assets/base/shaders/bokeh.vs b/assets/base/shaders/bokeh.vs new file mode 100644 index 0000000..dad0f5a --- /dev/null +++ b/assets/base/shaders/bokeh.vs @@ -0,0 +1,44 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; + +uniform mat4 u_mvpMtx; +uniform sampler2D u_texture; +uniform float u_size; + +varying vec4 v_col; + +//const float bright = 0.1; +//const float size = 21.5; + +const float ndofstart = 1.0; +const float ndofdist = 2.0; +const float fdofstart = 1.0; +const float fdofdist = 3.0; + +void main() +{ + float depth = abs(a_position.x*3.0); + float fDepth = 1.5; + float a = depth-fDepth; //focal plane + float b = (a-fdofstart)/fdofdist; //far DoF + float c = (-a-ndofstart)/ndofdist; //near Dof + float size = (a>0.0)?b:c; + size = clamp(size,0.0,1.0) *20.0+1.0; + //float size = length(a_position.xy-0.5)*20.0+1.0; + //float size = abs(a_position.x-u_size*0.01-0.4)*64.5+5.0; + //float size=7.0; + //size = u_size;//*0.5+3.0; + + //4/PI + float bright = 1.273239/(size*size); + + bright = clamp(bright,0.007,1.0); + gl_Position = vec4(a_position.xyz*2.0-1.0,1.0); + v_col = texture2D(u_texture,a_position.xy)*bright; + if(length(v_col)<0.01) + gl_Position.z=-999.9; + gl_PointSize = size; + //gl_PointSize = a_position.x*34.5+5.0; +} diff --git a/assets/base/shaders/col_tex.fs b/assets/base/shaders/col_tex.fs new file mode 100644 index 0000000..9b52ac8 --- /dev/null +++ b/assets/base/shaders/col_tex.fs @@ -0,0 +1,12 @@ +#version 100 +precision highp float; + +varying vec2 v_uv; +uniform sampler2D u_texture; +uniform vec4 u_color; + +void main() +{ + vec4 col = texture2D(u_texture, v_uv); + gl_FragColor = col * u_color; +} diff --git a/assets/base/shaders/generic.vs b/assets/base/shaders/generic.vs new file mode 100644 index 0000000..74c6be1 --- /dev/null +++ b/assets/base/shaders/generic.vs @@ -0,0 +1,23 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; +attribute vec3 a_normal; +attribute vec2 a_uv; + +varying vec3 v_normal; +varying vec2 v_uv; +varying vec3 v_viewDir; + +uniform mat4 u_mvpMtx; +uniform mat4 u_modelMtx; +uniform vec3 u_camPos; + +void main() +{ + gl_Position = u_mvpMtx * a_position; + gl_PointSize = 4.0; + v_normal = normalize(mat3(u_modelMtx)*a_normal); + v_uv = a_uv; + v_viewDir = u_camPos-(u_modelMtx*a_position).xyz; +} diff --git a/assets/base/shaders/lightDir.fs b/assets/base/shaders/lightDir.fs new file mode 100644 index 0000000..4a17a47 --- /dev/null +++ b/assets/base/shaders/lightDir.fs @@ -0,0 +1,32 @@ +#version 100 +precision highp float; + +varying vec3 v_normal; +varying vec2 v_uv; +varying vec3 v_viewDir; + +uniform sampler2D u_tex; +uniform vec3 u_lightDir; + +const float specPower = 40.0; + +vec3 calcLight(vec3 dir, vec3 norm, vec3 lcol, vec3 col){ + vec3 reflectVec = reflect(-dir, norm); + float diffuse = max(0.0,dot(norm,dir)); + vec3 vd = normalize(v_viewDir); + float specular = 0.5*pow(max(dot(vd, reflectVec), 0.0), specPower); + + return col*diffuse*lcol + specular*lcol; +} + +void main() +{ + vec4 col = texture2D(u_tex, v_uv); + + vec3 normal = normalize(v_normal); + gl_FragColor = vec4(0.0); + //float diffuse = max(0.0,dot(normal,u_lightDir)); + gl_FragColor.rgb = calcLight(u_lightDir,normal,vec3(1.0),col.rgb); + //gl_FragColor = col; + gl_FragColor.rgb+=0.1; +} diff --git a/assets/base/shaders/lightPoint.fs b/assets/base/shaders/lightPoint.fs new file mode 100644 index 0000000..810e411 --- /dev/null +++ b/assets/base/shaders/lightPoint.fs @@ -0,0 +1,43 @@ +#version 100 +precision highp float; + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_uv; + +uniform sampler2D u_texture; +uniform vec3 u_lightColor; +uniform vec3 u_lightPos; +uniform float u_lightSize; + +varying vec3 v_viewDir; +const float specPower = 10.0; + +vec3 calcLight(vec3 dir, vec3 norm, vec3 lcol, vec3 col){ + vec3 reflectVec = reflect(-dir, norm); + float diffuse = max(0.0,dot(norm,dir)); + vec3 vd = normalize(v_viewDir); + float specular = 0.3*pow(max(dot(vd, reflectVec), 0.0), specPower); + + return col*diffuse*lcol + specular*lcol; +} + +vec3 calcPointLight(vec3 pos, vec3 norm, vec3 lcol, vec3 col){ + //float atten = 1.0 - pow(clamp(length(pos-v_position)/u_lightSize, 0.0, 1.0), 1.5); + float atten = 1.0-clamp(length(pos-v_position)/u_lightSize, 0.0, 1.0); + return calcLight(normalize(pos-v_position),norm,lcol,col)*atten; +} + +void main() +{ + //if(length(u_lightPos-v_position)>u_lightSize) + // discard; + vec4 col = texture2D(u_texture, v_uv); + vec3 norm = normalize(v_normal); + + vec4 outc = vec4(0.0,0.0,0.0,1.0); + outc.rgb += calcPointLight(u_lightPos,norm,u_lightColor,col.rgb); + + gl_FragColor = outc; +} + diff --git a/assets/base/shaders/lightPoint.vs b/assets/base/shaders/lightPoint.vs new file mode 100644 index 0000000..ca95d00 --- /dev/null +++ b/assets/base/shaders/lightPoint.vs @@ -0,0 +1,25 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; +attribute vec3 a_normal; +attribute vec2 a_uv; + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_uv; +varying vec3 v_viewDir; + +uniform mat4 u_mvpMtx; +uniform mat4 u_modelMtx; +uniform vec3 u_camPos; + +void main() +{ + gl_Position = u_mvpMtx * a_position; + gl_PointSize = 4.0; + v_normal = normalize(mat3(u_modelMtx)*a_normal); + v_position = (u_modelMtx*a_position).xyz; + v_uv = a_uv; + v_viewDir = u_camPos-(u_modelMtx*a_position).xyz; +} diff --git a/assets/base/shaders/null.fs b/assets/base/shaders/null.fs new file mode 100644 index 0000000..18eb476 --- /dev/null +++ b/assets/base/shaders/null.fs @@ -0,0 +1,6 @@ +#version 100 +precision highp float; + +void main(){ + gl_FragColor = vec4(1.0); +} diff --git a/assets/base/shaders/quad.fs b/assets/base/shaders/quad.fs new file mode 100644 index 0000000..80f08c5 --- /dev/null +++ b/assets/base/shaders/quad.fs @@ -0,0 +1,10 @@ +precision highp float; + +varying vec2 v_uv; +uniform sampler2D u_tex; + +void main() +{ + gl_FragColor.rgb = vec3(0.5); + gl_FragColor.a = 0.2;//texture2D(u_tex,v_uv); +} diff --git a/assets/base/shaders/quad.vs b/assets/base/shaders/quad.vs new file mode 100644 index 0000000..e6262e5 --- /dev/null +++ b/assets/base/shaders/quad.vs @@ -0,0 +1,13 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; +varying vec2 v_uv; +uniform vec4 u_transform; + +void main() +{ + gl_Position = a_position; + gl_Position.xy = a_position.xy*u_transform.zw+u_transform.xy; + v_uv = a_position.xy*0.5+0.5; +} diff --git a/assets/base/shaders/simple.fs b/assets/base/shaders/simple.fs new file mode 100644 index 0000000..fe6a307 --- /dev/null +++ b/assets/base/shaders/simple.fs @@ -0,0 +1,7 @@ +#version 100 +precision highp float; + +void main() +{ + gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0); +} \ No newline at end of file diff --git a/assets/base/shaders/simple.vs b/assets/base/shaders/simple.vs new file mode 100644 index 0000000..a2600ad --- /dev/null +++ b/assets/base/shaders/simple.vs @@ -0,0 +1,12 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; + +uniform mat4 u_mvpMtx; + +void main() +{ + gl_Position = u_mvpMtx * a_position; + gl_PointSize = 4.0; +} diff --git a/assets/base/shaders/skybox.fs b/assets/base/shaders/skybox.fs new file mode 100644 index 0000000..4542f65 --- /dev/null +++ b/assets/base/shaders/skybox.fs @@ -0,0 +1,11 @@ +#version 100 +precision highp float; + +varying vec3 v_dir; +uniform samplerCube u_tex; + +void main() +{ + vec4 col = textureCube(u_tex, v_dir); + gl_FragColor = col; +} diff --git a/assets/base/shaders/skybox.vs b/assets/base/shaders/skybox.vs new file mode 100644 index 0000000..f288f73 --- /dev/null +++ b/assets/base/shaders/skybox.vs @@ -0,0 +1,10 @@ +#version 100 +attribute vec4 a_position; +varying vec3 v_dir; +uniform mat4 u_mvpMtx; + +void main() +{ + gl_Position = u_mvpMtx * a_position; + v_dir = a_position.xyz; +} diff --git a/assets/blur/SearchPaths.txt b/assets/blur/SearchPaths.txt new file mode 100644 index 0000000..e03b7ab --- /dev/null +++ b/assets/blur/SearchPaths.txt @@ -0,0 +1,2 @@ +1 +base diff --git a/assets/blur/shaders/tex_mip.fs b/assets/blur/shaders/tex_mip.fs new file mode 100644 index 0000000..6c4b7d0 --- /dev/null +++ b/assets/blur/shaders/tex_mip.fs @@ -0,0 +1,14 @@ +#version 100 +precision highp float; + +varying vec2 v_uv; +uniform sampler2D u_texture; + +void main() +{ + float a=0.0; + //a=length(v_uv-0.5)*5.0;//+4.0; + a=v_uv.x*2.0+3.5; + vec4 col = texture2D(u_texture, v_uv,a); + gl_FragColor = col; +} diff --git a/assets/pt/SearchPaths.txt b/assets/pt/SearchPaths.txt new file mode 100644 index 0000000..e03b7ab --- /dev/null +++ b/assets/pt/SearchPaths.txt @@ -0,0 +1,2 @@ +1 +base diff --git a/assets/pt/shaders/quad.fs b/assets/pt/shaders/quad.fs new file mode 100644 index 0000000..fa955dc --- /dev/null +++ b/assets/pt/shaders/quad.fs @@ -0,0 +1,7 @@ +#version 100 +precision highp float; +varying vec2 v_uv; +uniform sampler2D u_tex; +void main(){ + gl_FragColor = texture2D(u_tex,v_uv); +} \ No newline at end of file diff --git a/assets/pt/shaders/quad.vs b/assets/pt/shaders/quad.vs new file mode 100644 index 0000000..c2decfc --- /dev/null +++ b/assets/pt/shaders/quad.vs @@ -0,0 +1,8 @@ +#version 100 +precision highp float; +attribute vec4 a_position; +varying vec2 v_uv; +void main(){ + gl_Position = a_position; + v_uv = a_position.xy*0.5+0.5; +} diff --git a/assets/pt/shaders/quadTrace.fs b/assets/pt/shaders/quadTrace.fs new file mode 100644 index 0000000..332f00b --- /dev/null +++ b/assets/pt/shaders/quadTrace.fs @@ -0,0 +1,138 @@ +#version 100 +precision highp float; + +varying vec2 v_uv; +uniform float u_textureWeight; +uniform sampler2D texture; +uniform sampler2D u_worldTex; +uniform float u_timeSinceStart; +uniform int u_numCubes; +const float glossiness = 0.8; + +float intersectSphere(vec3 origin, vec3 ray, vec4 sphereCenter) +{ + vec3 toSphere = origin - sphereCenter.xyz; + float a = dot(ray, ray); + float b = 2.0 * dot(toSphere, ray); + float c = dot(toSphere, toSphere) - sphereCenter.w*sphereCenter.w; + float discriminant = b*b - 4.0*a*c; + if(discriminant > 0.0){ + float t = (-b - sqrt(discriminant)) / (2.0 * a); + if(t > 0.0) return t; + } + return 9999.9; +} +vec2 intersectCube(vec3 origin, vec3 ray, vec3 cubeMin, vec3 cubeMax){ + vec3 tMin = (cubeMin - origin) / ray; + vec3 tMax = (cubeMax - origin) / ray; + vec3 t1 = min(tMin, tMax); + vec3 t2 = max(tMin, tMax); + float tNear = max(max(t1.x, t1.y), t1.z); + float tFar = min(min(t2.x, t2.y), t2.z); + return vec2(tNear, tFar); +} +vec3 normalForCube(vec3 hit, vec3 cubeMin, vec3 cubeMax){ + if(hit.x < cubeMin.x + 0.0001) return vec3(-1.0, 0.0, 0.0); + else if(hit.x > cubeMax.x - 0.0001) return vec3(1.0, 0.0, 0.0); + else if(hit.y < cubeMin.y + 0.0001) return vec3(0.0, -1.0, 0.0); + else if(hit.y > cubeMax.y - 0.0001) return vec3(0.0, 1.0, 0.0); + else if(hit.z < cubeMin.z + 0.0001) return vec3(0.0, 0.0, -1.0); + else return vec3(0.0, 0.0, 1.0); +} +float random(vec3 scale, float seed){ + return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); +} +vec3 cosineWeightedDirection(float seed, vec3 normal){ + float u = random(vec3(12.9898, 78.233, 151.7182), seed); + float v = random(vec3(63.7264, 10.873, 623.6736), seed); + float r = sqrt(u); + float angle = 6.283185307179586 * v; + vec3 sdir, tdir; + if (abs(normal.x)<.5){ + sdir = cross(normal, vec3(1,0,0)); + }else{ + sdir = cross(normal, vec3(0,1,0)); + } + tdir = cross(normal, sdir); + return r*cos(angle)*sdir + r*sin(angle)*tdir + sqrt(1.-u)*normal; +} +vec3 uniformlyRandomDirection(float seed){ + float u = random(vec3(12.9898, 78.233, 151.7182), seed); + float v = random(vec3(63.7264, 10.873, 623.6736), seed); + float z = 1.0 - 2.0 * u; + float r = sqrt(1.0 - z * z); + float angle = 6.283185307179586 * v; + return vec3(r * cos(angle), r * sin(angle), z); +} +vec3 uniformlyRandomVector(float seed){ + return uniformlyRandomDirection(seed) * sqrt(random(vec3(36.7539, 50.3658, 306.2759), seed)); +} +float shadow(vec3 origin, vec3 ray) { + for(int c = 0; c 0.0 && tCube1.x < 1.0 && tCube1.x < tCube1.y) + return 0.0; + } + return 1.0; +} +const vec3 lightPos = vec3(-1.3,0.1,0.05); +vec3 calculateColor(vec3 origin, vec3 ray, vec3 light,float seed){ + vec3 colorMask = vec3(1.0); + vec3 result = vec3(0.0); + for(int bounce = 0; bounce < 4; bounce++){ + float t = 9999.9; + vec3 normal; + vec2 tRoom = intersectCube(origin, ray, vec3(-1.5,-1.0,-1.0), vec3(1.0)); + if(tRoom.x < tRoom.y) t = tRoom.y; + vec3 hit = origin + ray * t; + for(int c = 0; c < u_numCubes;c++){ + vec3 cubemin = texture2D(u_worldTex,vec2(0.0,float(c)/float(u_numCubes-1))).xyz; + vec3 cubemax = texture2D(u_worldTex,vec2(1.0,float(c)/float(u_numCubes-1))).xyz; + vec2 tCube1 = intersectCube(origin, ray, cubemin,cubemax); + if(tCube1.x > 0.0 && tCube1.x < t && tCube1.x < tCube1.y) + { + t = tCube1.x; + hit = origin + ray * t; + normal = normalForCube(hit, cubemin,cubemax); + } + } + float lth = intersectSphere(origin,ray,vec4(lightPos,0.1)); + if(lth 0.9999) surfaceColor = vec3(0.3, 1.0, 0.1); + ray = cosineWeightedDirection(seed + float(bounce), normal); + //ray = reflect(ray, normal); + //ray = normalize(reflect(ray, normal)) + uniformlyRandomVector(seed + float(bounce)) * glossiness; + }else if(t == 9999.9){ + break; + }else{ + ray = normalize(reflect(ray, normal)) + uniformlyRandomVector(seed + float(bounce)) * glossiness; + surfaceColor = vec3(0.5, 0.5, 0.9); + } + vec3 toLight = light - hit; + float diffuse = max(0.0, dot(normalize(toLight), normal)); + float shadowIntensity = shadow(hit + normal * 0.0001, toLight); + colorMask *= surfaceColor; + //if(bounce>0) + result += colorMask*diffuse*0.5*shadowIntensity; + origin = hit; + } + return result; +} +void main(){ + vec3 dir = normalize(vec3(v_uv,-1.0)); + vec3 newLight = lightPos + uniformlyRandomVector(u_timeSinceStart - 53.0) * 0.1; + vec3 sample1 = calculateColor(vec3(-0.2,0.15,1.5), dir, newLight,u_timeSinceStart); + //newLight = vec3(-1.4,0.1,-0.1) + uniformlyRandomVector(u_timeSinceStart - 27.4) * 0.1; + //sample1 = 0.5*(sample1+calculateColor(vec3(-0.2,0.15,1.5), dir, newLight,u_timeSinceStart+65.3)); + vec3 textureCol = texture2D(texture, v_uv*0.5+0.5).rgb; + gl_FragColor = vec4(mix(sample1, textureCol, u_textureWeight), 1.0); + + //gl_FragColor = vec4(sample1,1.0); +} diff --git a/assets/pt/shaders/quadTrace.vs b/assets/pt/shaders/quadTrace.vs new file mode 100644 index 0000000..f82fa7d --- /dev/null +++ b/assets/pt/shaders/quadTrace.vs @@ -0,0 +1,8 @@ +#version 100 +precision highp float; +attribute vec4 a_position; +varying vec2 v_uv; +void main(){ + gl_Position = a_position; + v_uv = a_position.xy; +} diff --git a/assets/pt/shaders/tex.fs b/assets/pt/shaders/tex.fs new file mode 100644 index 0000000..fc9744f --- /dev/null +++ b/assets/pt/shaders/tex.fs @@ -0,0 +1,9 @@ +#version 100 +precision highp float; + +varying vec2 v_uv; +uniform sampler2D u_tex; + +void main(){ + gl_FragColor = texture2D(u_tex,v_uv); +} \ No newline at end of file diff --git a/assets/pt/shaders/tex.vs b/assets/pt/shaders/tex.vs new file mode 100644 index 0000000..0c02f49 --- /dev/null +++ b/assets/pt/shaders/tex.vs @@ -0,0 +1,16 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; +attribute vec2 a_uv; + +varying vec2 v_uv; + +uniform mat4 u_mvpMtx; + +void main() +{ + gl_Position = u_mvpMtx * a_position; + v_uv = a_uv; + gl_PointSize = 4.0; +} diff --git a/assets/skinning/SearchPaths.txt b/assets/skinning/SearchPaths.txt new file mode 100644 index 0000000..b8eb9e4 --- /dev/null +++ b/assets/skinning/SearchPaths.txt @@ -0,0 +1,3 @@ +1 +base + diff --git a/assets/skinning/config.txt b/assets/skinning/config.txt new file mode 100644 index 0000000..3cae356 --- /dev/null +++ b/assets/skinning/config.txt @@ -0,0 +1,11 @@ +scale 0.03 0.03 0.03 +model models/turret.mdl +anim 0 +#model models/Antlion.mdl +#model models/Alyx.mdl +#model models/personality_sphere.mdl +#model models/elevator_b.mdl +#model models/player.mdl +#model models/player_animations.mdl +#model models/headcrabclassic.mdl +#model models/male_07.mdl diff --git a/assets/skinning/materials/place materials here.txt b/assets/skinning/materials/place materials here.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/skinning/models/place models here.txt b/assets/skinning/models/place models here.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/skinning/shaders/skinning.fs b/assets/skinning/shaders/skinning.fs new file mode 100644 index 0000000..7a9f4af --- /dev/null +++ b/assets/skinning/shaders/skinning.fs @@ -0,0 +1,17 @@ +#version 100 +precision highp float; + +varying vec3 v_normal; +varying vec2 v_uv; +varying vec3 v_bones; + +uniform sampler2D u_texture; +uniform vec4 u_color; + +void main() +{ + vec4 col = texture2D(u_texture, v_uv); + col *= max(0.2,dot(v_normal,normalize(vec3(1.0)))); + gl_FragColor = col * u_color; + //gl_FragColor = mix(gl_FragColor,v_bones.xxxx*0.1,0.99); +} diff --git a/assets/skinning/shaders/skinning.vs b/assets/skinning/shaders/skinning.vs new file mode 100644 index 0000000..d0daaf3 --- /dev/null +++ b/assets/skinning/shaders/skinning.vs @@ -0,0 +1,32 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; +attribute vec3 a_normal; +attribute vec2 a_uv; +attribute vec3 a_weight; +attribute vec3 a_bones; + +varying vec3 v_normal; +varying vec2 v_uv; +//varying vec3 v_viewDir; +varying vec3 v_bones; + +uniform mat4 u_mvpMtx; +uniform mat4 u_modelMtx; +uniform mat4 u_bonesMtx[77]; +//uniform vec3 u_camPos; + +void main(){ + /*int bi = int(a_bones.x); + if(bi>76)bi=0; + mat4 boneMtx = u_bonesMtx[bi];//*a_weight.x;*/ + mat4 boneMtx = u_bonesMtx[int(a_bones.x)]*a_weight.x+u_bonesMtx[int(a_bones.y)]*a_weight.y+u_bonesMtx[int(a_bones.z)]*a_weight.z; + gl_Position = u_mvpMtx * boneMtx * a_position; + gl_PointSize = 4.0; + v_normal = normalize(mat3(u_modelMtx * boneMtx)*a_normal); + v_uv = a_uv; + //v_viewDir = u_camPos-(u_modelMtx*a_position).xyz; + v_bones=a_bones; +} + diff --git a/assets/ssr/SearchPaths.txt b/assets/ssr/SearchPaths.txt new file mode 100644 index 0000000..e03b7ab --- /dev/null +++ b/assets/ssr/SearchPaths.txt @@ -0,0 +1,2 @@ +1 +base diff --git a/assets/ssr/shaders/ssr.fs b/assets/ssr/shaders/ssr.fs new file mode 100644 index 0000000..dc90e1f --- /dev/null +++ b/assets/ssr/shaders/ssr.fs @@ -0,0 +1,170 @@ +#version 100 +precision highp float; + +varying vec4 v_scrpos; +varying vec4 v_position; +varying vec3 v_normal; +uniform sampler2D u_tex; +uniform sampler2D u_depthTex; +uniform mat4 u_mvpMtx; +uniform mat4 u_invMVPMtx; + +uniform vec3 u_eyePos;// = vec3(0.0, 0.6, 1.0); + +vec3 GetPos(vec2 uv, float depth) +{ + vec4 pos = u_invMVPMtx*(vec4(uv,depth,1.0)*2.0-1.0); + pos.xyz /= pos.w; + return pos.xyz; +} + +vec3 GetUV(vec3 apos) +{ + vec4 pVP = u_mvpMtx*vec4(apos,1.0); + pVP.xy = vec2(0.5,0.5)+vec2(0.5,0.5)*pVP.xy / pVP.w; + return vec3(pVP.xy,pVP.z/pVP.w); +} + +vec4 u_camera = vec4(0.1,2.0,0.5,1.0);//near far fov aspect +float GetDepth(vec2 uv) +{ + float depth = texture2D(u_depthTex,uv).r; + depth = 2.0 * u_camera.x * u_camera.y / + (u_camera.y + u_camera.x - + (depth*2.0-1.0)*(u_camera.y-u_camera.x)); + return depth; +} + +const float rayStepSize = 0.1; +vec2 v_uv; +vec3 raytrace(in vec3 reflectionVector, in float startDepth) +{ + vec3 color = vec3(0.0); + float stepSize = rayStepSize; + + float size = length(reflectionVector.xy); + reflectionVector = normalize(reflectionVector/size); + reflectionVector = reflectionVector * stepSize; + + //Current sampling position is at current fragment + vec2 sampledPosition = v_uv; + //Current depth at current fragment + float currentDepth = startDepth; + //The sampled depth at the current sampling position + //float sampledDepth = linearizeDepth(texture2D(u_depthTexture, sampledPosition).x); + float sampledDepth = GetDepth(sampledPosition); + + // Raytrace as long as in texture space of depth buffer (between 0 and 1) + while(sampledPosition.x <= 1.0 && sampledPosition.x >= 0.0 && sampledPosition.y <= 1.0 && sampledPosition.y >= 0.0) + { + //Update sampling position by adding reflection vector's xy and y components + sampledPosition = sampledPosition + reflectionVector.xy; + //Updating depth values + currentDepth = currentDepth + reflectionVector.z * startDepth; + //float sampledDepth = linearizeDepth( texture2D(u_depthTexture, sampledPosition).x); + float sampledDepth = GetDepth(sampledPosition); + + //If current depth is greater than sampled depth of depth buffer, intersection is found + if(currentDepth > sampledDepth) + { + //Delta is for stop the raytracing after the first intersection is found + //Not using delta will create "repeating artifacts" + float delta = (currentDepth - sampledDepth); + if(delta < 0.003) + { + color = texture2D(u_tex, v_uv).rgb; + break; + } + } + } + + return color; +} + +#if 0 +vec4 ssr() +{ + vec3 reflectedColor = vec3(0.0); + + //vec3 normal = normalize( texture2D(u_normalTexture, v_uv).xyz*2.0-1.0 ); + vec3 normal = v_normal; + + //Depth at current fragment + //float currDepth = linearizeDepth( texture2D(u_depthTex, v_uv).x ); + float currDepth = GetDepth(v_uv); + + vec3 pos = GetPos(v_uv,currDepth); + //Eye position, camera is at (0, 0, 0), we look along negative z, add near plane to correct parallax + vec3 eyePosition = normalize(pos-u_eyePos);// vec3(0.0, 0.0, 0.01) ); + vec3 reflectionVector = reflect(eyePosition, normal); + reflectionVector= GetUV(reflectionVector); + //Call raytrace to get reflected color + reflectedColor = raytrace(reflectionVector, currDepth); + + float nl = max(-dot(normal, eyePosition),0.0); + float fresnel = pow(1.0-nl,0.82); + + return vec4(reflectedColor, 1.0); +} + +#else +vec4 ssr() +{ + //todo: normal buffer + vec3 normal = v_normal; + + //vec3 pos = GetPos(uv, depth); + vec3 pos = v_position.xyz; + vec3 viewDir = normalize(pos-u_eyePos); + vec3 reflectDir = normalize(reflect(viewDir, normal)); + + vec3 currentRay = vec3(0.0); + vec3 nuv = vec3(0.0); + float L = 0.06;//??? + + for(int i = 0; i<10; i++){ + currentRay = pos+reflectDir*L; + + nuv = GetUV(currentRay); + float nd = texture2D(u_depthTex, nuv.xy).x; + vec3 newPos = GetPos(nuv.xy, nd);//2??? + L = length(pos-newPos); + } + if(L>0.4) + return vec4(0.0); + vec4 cnuv = vec4(0.0); + cnuv = texture2D(u_tex, nuv.xy); + /*for(float x = -0.1; x<=0.1; x+=0.025){ + cnuv += texture2D(u_tex, nuv.xy+vec2(x*L,0.0))*0.1; + }*/ + + float nl = max(-dot(normal, viewDir),0.0); + float fresnel = pow(1.0-nl,0.82); + + //return vec4(normalize(currentRay),1.0); + //return vec4(vec3(pow(nuv.z,0.4)),1.0); + //return vec4(L*4.0); + return cnuv*fresnel*(1.0-L*2.0); +// return vec4(L*4.0); +} +#endif + +void main() +{ + vec2 uv = v_scrpos.xy/v_scrpos.w*0.5+0.5; + v_uv = uv; + vec4 col = texture2D(u_tex, uv); + //float depth = texture2D(u_depthTex, uv).x; + float depth = gl_FragCoord.z; + vec4 reflect = vec4(0.0); + if(depth<(texture2D(u_depthTex, uv).x+0.002)) + { + reflect = ssr(); + } + + gl_FragColor = col+reflect; + //gl_FragColor = reflect; + + //gl_FragColor = col*pow(depth, 10.0); + //gl_FragColor = vec4(reflectDir, 1.0); +} diff --git a/assets/ssr/shaders/ssr.vs b/assets/ssr/shaders/ssr.vs new file mode 100644 index 0000000..df66c15 --- /dev/null +++ b/assets/ssr/shaders/ssr.vs @@ -0,0 +1,19 @@ +#version 100 +precision highp float; + +attribute vec4 a_position; +attribute vec3 a_normal; + +uniform mat4 u_mvpMtx; + +varying vec4 v_scrpos; +varying vec4 v_position; +varying vec3 v_normal; + +void main() +{ + v_scrpos = u_mvpMtx * a_position; + v_position = a_position; + v_normal = a_normal; + gl_Position = u_mvpMtx * a_position; +} \ No newline at end of file diff --git a/assets/star-fleet/SearchPaths.txt b/assets/star-fleet/SearchPaths.txt new file mode 100644 index 0000000..e03b7ab --- /dev/null +++ b/assets/star-fleet/SearchPaths.txt @@ -0,0 +1,2 @@ +1 +base diff --git a/assets/star-fleet/models/sship1.nmf b/assets/star-fleet/models/sship1.nmf new file mode 100644 index 0000000..c383360 Binary files /dev/null and b/assets/star-fleet/models/sship1.nmf differ diff --git a/assets/tpsGame/SearchPaths.txt b/assets/tpsGame/SearchPaths.txt new file mode 100644 index 0000000..b8eb9e4 --- /dev/null +++ b/assets/tpsGame/SearchPaths.txt @@ -0,0 +1,3 @@ +1 +base + diff --git a/assets/tpsGame/materials/basetexture.vtf b/assets/tpsGame/materials/basetexture.vtf new file mode 100644 index 0000000..21e8379 Binary files /dev/null and b/assets/tpsGame/materials/basetexture.vtf differ diff --git a/assets/tpsGame/materials/light.txt b/assets/tpsGame/materials/light.txt new file mode 100644 index 0000000..f930a33 --- /dev/null +++ b/assets/tpsGame/materials/light.txt @@ -0,0 +1,2 @@ +light +basetexture.vtf diff --git a/assets/tpsGame/materials/metal1.txt b/assets/tpsGame/materials/metal1.txt new file mode 100644 index 0000000..f930a33 --- /dev/null +++ b/assets/tpsGame/materials/metal1.txt @@ -0,0 +1,2 @@ +light +basetexture.vtf diff --git a/assets/tpsGame/materials/metal_patterned_01-l-color.dds b/assets/tpsGame/materials/metal_patterned_01-l-color.dds new file mode 100644 index 0000000..f82ab6e Binary files /dev/null and b/assets/tpsGame/materials/metal_patterned_01-l-color.dds differ diff --git a/assets/tpsGame/materials/metal_patterned_01.txt b/assets/tpsGame/materials/metal_patterned_01.txt new file mode 100644 index 0000000..90bd063 --- /dev/null +++ b/assets/tpsGame/materials/metal_patterned_01.txt @@ -0,0 +1,3 @@ +light +materials/metal_patterned_01-l-color.dds +materials/metal_patterned_01-l-normal.dds diff --git a/assets/tpsGame/materials/sship1.txt b/assets/tpsGame/materials/sship1.txt new file mode 100644 index 0000000..da2426b --- /dev/null +++ b/assets/tpsGame/materials/sship1.txt @@ -0,0 +1,2 @@ +light +basetexture.vtf \ No newline at end of file diff --git a/assets/tpsGame/models/char1.nmf b/assets/tpsGame/models/char1.nmf new file mode 100644 index 0000000..e99b057 Binary files /dev/null and b/assets/tpsGame/models/char1.nmf differ diff --git a/assets/tpsGame/models/map01.nmf b/assets/tpsGame/models/map01.nmf new file mode 100644 index 0000000..f413401 Binary files /dev/null and b/assets/tpsGame/models/map01.nmf differ diff --git a/assets/tpsGame/models/map02.nmf b/assets/tpsGame/models/map02.nmf new file mode 100644 index 0000000..e217fea Binary files /dev/null and b/assets/tpsGame/models/map02.nmf differ diff --git a/assets/tpsGame/models/pistol1.nmf b/assets/tpsGame/models/pistol1.nmf new file mode 100644 index 0000000..c8caceb Binary files /dev/null and b/assets/tpsGame/models/pistol1.nmf differ diff --git a/assets/tpsGame/models/spaceport1_p.nmf b/assets/tpsGame/models/spaceport1_p.nmf new file mode 100644 index 0000000..70c99c2 Binary files /dev/null and b/assets/tpsGame/models/spaceport1_p.nmf differ diff --git a/assets/tpsGame/models/spaceport1_r.nmf b/assets/tpsGame/models/spaceport1_r.nmf new file mode 100644 index 0000000..2b8905b Binary files /dev/null and b/assets/tpsGame/models/spaceport1_r.nmf differ diff --git a/assets/tpsGame/models/sphere.nmf b/assets/tpsGame/models/sphere.nmf new file mode 100644 index 0000000..37ac4c7 Binary files /dev/null and b/assets/tpsGame/models/sphere.nmf differ diff --git a/assets/tpsGame/models/sship1_s.nmf b/assets/tpsGame/models/sship1_s.nmf new file mode 100644 index 0000000..e70d158 Binary files /dev/null and b/assets/tpsGame/models/sship1_s.nmf differ diff --git a/assets/tpsGame/scenes/test.txt b/assets/tpsGame/scenes/test.txt new file mode 100644 index 0000000..d2d9ff7 --- /dev/null +++ b/assets/tpsGame/scenes/test.txt @@ -0,0 +1,156 @@ +1 +-1 3 8 0 50 0 +33 + +StaticModel sphere.nmf 30 40 70 0 0 0 0 + +Button +-2 1.5 3 +enter_ship enter_ship +tp -20 0 3.5 15 20 0 +Button +-19.5 1.5 8 +leave_ship leave_ship +tp -8 1 3 15 270 0 + +Door +-20 1.2 4.65 +door1 1 1.2 0.08 +Button +-20 1.5 4.5 +open_door1 open_door1 +use door1 + +Light 2 +-20.4 1.6 3.5 +0.8 0.8 1 +7 + +StaticModel sship1_s.nmf -16 0 -22 0 130 0 0 +Light 2 +-14 1 -25 +1 0.55 0.4 +8 +Collider +-16.3 1 -21.6 0 130 0 +1 1.8 0.8 3.5 + +StaticModel spaceport1_p.nmf -16 0 -14 0 90 0 +0 +StaticModel spaceport1_r.nmf -16 0 -14 0 90 0 +0 +Collider +-16 -0.1 -19.5 0 0 0 +1 6 0.1 6.5 +StaticModel map02.nmf +0 0 0 0 0 0 +0 +Collider +0 -0.1 0 0 0 0 +1 10 0.1 10 +Collider +-5 2 -6 0 0 0 +1 2 2 3 +Collider +6 2.5 -3 0 0 0 +1 3 2.5 6 +StaticModel map02.nmf +0 0 -20 0 0 0 +0 +Collider +0 -0.1 -20 0 0 0 +1 10 0.1 10 +Collider +-5 2 -26 0 0 0 +1 2 2 3 +Collider +6 2.5 -23 0 0 0 +1 3 2.5 6 +StaticModel cube.nmf +1 -0.1 -2.5 20 0 0 +1 1 1 1 +StaticModel bridge1.nmf +-20 0 0 0 0 0 +0 +StaticModel map01.nmf +-21 0 8.7 0 0 0 +0 +Collider +-20 -0.1 14 0 0 0 +1 3 0.1 14 +Collider +-20 2.1 14 0 0 0 +1 3 0.1 14 +Collider +-20 2 1 0 0 0 +1 3 2 0.1 +Collider +-20 0.5 2.5 0 0 0 +1 0.5 0.5 0.5 +Collider +-23 2 3 0 0 0 +1 0.1 2 2 +Collider +-17 2 3 0 0 0 +1 0.1 2 2 +Collider +-18 2 9.5 0 0 0 +1 1 2 5 +Collider +-22 2 9.5 0 0 0 +1 1 2 5 +Collider +-20 2 15 0 0 0 +1 3 2 0.1 + + +#stairs +StaticModel cube.nmf +-2.5 -0.8 0 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-2.51 -0.6 -0.8 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-2.52 -0.4 -1.6 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-2.53 -0.2 -2.4 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-2.54 0 -3.2 0 0 0 +1 1 1 1 + +StaticModel cube.nmf +-4.5 -0.7 0 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-4.51 -0.4 -0.8 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-4.52 -0.1 -1.6 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-4.53 0.2 -2.4 0 0 0 +1 1 1 1 +StaticModel cube.nmf +-4.54 0.5 -3.2 0 0 0 +1 1 1 1 + + + +-20 0 3.5 15 60 0 +5 +StaticModel map02.nmf +0 0 0 0 0 0 +0 +StaticModel map02.nmf +0 0 -13.8 0 0 0 +0 +StaticModel city1.nmf +13 0 -13.8 0 0 0 +0 +StaticModel house-small.nmf +0 -1 -26 0 0 0 +0 + diff --git a/assets/tpsGame/tpsConfig.txt b/assets/tpsGame/tpsConfig.txt new file mode 100644 index 0000000..605b043 --- /dev/null +++ b/assets/tpsGame/tpsConfig.txt @@ -0,0 +1,8 @@ +view tps +drawTouch 1 +debugPhysics 0 +drawBbox 0 +startScene test +screenScale 1 +pistOffs 0.1 -0.3 -0.3 + diff --git a/assets/vol_light/SearchPaths.txt b/assets/vol_light/SearchPaths.txt new file mode 100644 index 0000000..b8eb9e4 --- /dev/null +++ b/assets/vol_light/SearchPaths.txt @@ -0,0 +1,3 @@ +1 +base + diff --git a/assets/vol_light/config.txt b/assets/vol_light/config.txt new file mode 100644 index 0000000..bd6eaf5 --- /dev/null +++ b/assets/vol_light/config.txt @@ -0,0 +1,19 @@ +modelName bridge1.nmf +modelPos 0 1 0 +lightPos 1.1 7.1 -4 +lightTarget 0 2 2 +lightFOV 40 +lightAspect 1 +lightFar 12 +lightNear 4 +cameraPos 2.9 2.5 1.5 +cameraRot 10 100 0 +lightRes 512 +volScale 1 +debug 0 + +lightPos 0.4 3.1 -2 +lightFOV 60 +lightAspect 1.2 +lightNear 0.2 +lightFar 8 \ No newline at end of file diff --git a/assets/vol_light/shaders/volLight.fs b/assets/vol_light/shaders/volLight.fs new file mode 100644 index 0000000..031291a --- /dev/null +++ b/assets/vol_light/shaders/volLight.fs @@ -0,0 +1,114 @@ +#version 100 +precision highp float; + +varying vec4 v_position; +varying vec3 v_viewDir; +varying vec4 v_coord; + +uniform sampler2D u_lightDepth; +uniform sampler2D u_sceneDepth; +uniform mat4 u_lightMtx; +uniform mat4 u_invVPMtx; +uniform vec4 u_lightPosSize; + +vec3 GetPos(vec2 uv, float depth) +{ + vec4 pos = u_invVPMtx*(vec4(uv,depth,1.0)*2.0-1.0); + pos.xyz /= pos.w; + return pos.xyz; +} + +vec3 GetLightUV(vec3 pos){ + vec4 v = u_lightMtx * vec4(pos,1.0); + v.xyz /= v.w; + v.xyz = v.xyz*0.5+0.5; + return v.xyz; +} + +float GetDist(vec3 pos){ + vec3 luv = GetLightUV(pos); + float lightDepth = texture2D(u_lightDepth,luv.xy).r; + return luv.z-lightDepth; +} + +float GetShadow(vec3 pos){ + float shadow = GetDist(pos); + float atten = 1.0-clamp(length(u_lightPosSize.xyz-pos)/u_lightPosSize.w, 0.0, 1.0); + return float(shadow<0.001)*atten; +} + +float GetPCF(vec3 pos){ + vec3 luv = GetLightUV(pos); + float shadow = 25.0; + for(int x=-2; x<3; x++){ + for(int y=-2; y<2; y++){ + float lightDepth = texture2D(u_lightDepth,luv.xy+vec2(x,y)*0.002).r; + shadow -= float((luv.z-lightDepth)<0.003); + } + } + return shadow/25.0; +} + +#define STEPS 128.0 + +float rand(float n){ + return fract(sin(n) * 43758.5453123); +} + +float rand(vec2 n) { + return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); +} + +float noise(float p){ + float fl = floor(p); + float fc = fract(p); + return mix(rand(fl), rand(fl + 1.0), fc); +} + +float noise(vec2 n) { + const vec2 d = vec2(0.0, 1.0); + vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n)); + return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); +} + +void main(){ + vec3 scr = v_coord.xyz/v_coord.w; + scr.xyz = scr.xyz*0.5+0.5; + + float sceneDepth = texture2D(u_sceneDepth,scr.xy).r; + if(sceneDepth + +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/gl_ext.h" +#include "graphics/glsl_prog.h" +#include "graphics/ArrayBuffer.h" +#include "graphics/vao.h" +#include "graphics/texture.h" + +#ifdef ANDROID +#include "GLES2/gl2ext.h" +#else +#define GL_ETC1_RGB8_OES 0x8D64 +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 + +#endif + +#include "game/IGame.h" + +class ComprTexGame : public IGame{ +public: + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "base"; + } +}; + +IGame *CreateGame(){ + return new ComprTexGame(); +} + +float quadVerts[] = +{ + 1,-1, + 1, 1, + -1, 1, + -1,-1 +}; + +int scrWidth = 0; +int scrHeight = 0; +float aspect = 1.0f; + +glslProg progQuadTexture; +GLint u_quad_transform; +Texture etc1Texture; +Texture pvrTex; +VertexBufferObject vboQuad; +VertexArrayObject vaoQuad; + +void ComprTexGame::Created() +{ + CheckGLError("pre Created", __FILE__, __LINE__); + GLExtensions::Init(); + + progQuadTexture.CreateFromFile("quad", "col_tex"); + u_quad_transform = progQuadTexture.GetUniformLoc("u_transform"); + progQuadTexture.u_color = progQuadTexture.GetUniformLoc("u_color"); + progQuadTexture.Use(); + glUniform4f(progQuadTexture.u_color,1,1,1,1); + + glUseProgram(0); + CheckGLError("Created shaders", __FILE__, __LINE__); + + vboQuad.Create(); + vboQuad.Upload(2*4*4, quadVerts); + + vaoQuad.Create(); + vaoQuad.Bind(); + + vboQuad.Bind(); + vaoQuad.SetAttribute(0,2,GL_FLOAT,GL_FALSE,8,0); + + vaoQuad.Unbind(); + + vboQuad.Unbind(); + CheckGLError("Created meshes", __FILE__, __LINE__); + +#if 0 + GLubyte testTexData[] = + { + 0,0,0, 255,255,255, 0,0,0, 255,255,255, + 255,255,255, 0,0,0, 255,255,255, 0,0,0, + 0,0,0, 255,255,255, 0,0,0, 255,255,255, + 255,255,255, 0,0,0, 255,255,255, 0,0,0 + }; + etc1Texture.Create(4,4); + etc1Texture.Upload(0, GL_RGB, testTexData); +#else + GLubyte etc1Data[]={ + 0b01110111, + 0b01110111, + 0b01110111, + 0b11111100, + + 0b00000110, + 0b10100010, + 0b00001110, + 0b11101110 + }; + etc1Texture.Create(4,4); + etc1Texture.UploadCompressed(GL_COMPRESSED_RGB8_ETC2, 8, etc1Data);//GL_ETC1_RGB8_OES + CheckGLError("Upload etc1", __FILE__, __LINE__); +#endif +#if 0 + /*For PVRTC 4BPP formats the imageSize is calculated as: + ( max(width, 8) * max(height, 8) * 4 + 7) / 8 + For PVRTC 2BPP formats the imageSize is calculated as: + ( max(width, 16) * max(height, 8) * 2 + 7) / 8*/ + + //pvrtc block word (reversed) + //1 col a mode, 555 col a, 1 col b mode, 554 col b, 1 mode, 32 modul table (2b per sample) + GLubyte pvrData[32]={ + 255,255,0,0,0b11111110,0b11111111,0b00000000,0b11111100, + 255,255,0,0,0b11100000,0b11111111,0b11100000,0b10000011, + 255,255,0,0,0b00011110,0b11111100,0b00011111,0b10000000, + 255,255,0,0,0b11111110,0b10000011,0b00000000,0b10000000 + /* + 255,170,85,0,0b11111110,0b11111111,0b00000000,0b11111100, + 0,85,170,255,0b11100000,0b11111111,0b11100000,0b10000011, + 255,170,85,0,0b00011110,0b11111100,0b00011111,0b10000000, + 0,85,170,255,0b11111110,0b10000011,0b00000000,0b10000000 + */ + }; + + pvrTex.Create(8,8); + pvrTex.UploadCompressed(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 32, pvrData); + CheckGLError("Upload pvr", __FILE__, __LINE__); +#endif + + glBindTexture(GL_TEXTURE_2D, 0); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); +// glEnable(GL_CULL_FACE); + CheckGLError("Created", __FILE__, __LINE__); +} + +void ComprTexGame::Changed(int w, int h) +{ + scrWidth = w; + scrHeight = h; + glViewport(0, 0, w, h); + aspect = w/(float)h; +} + +void ComprTexGame::Draw() +{ + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + CheckGLError("Clear", __FILE__, __LINE__); + + vaoQuad.Bind(); + CheckGLError("Bind plane", __FILE__, __LINE__); + + progQuadTexture.Use(); + glUniform4f(u_quad_transform, 0, 0, 0.8, 0.8*aspect); + CheckGLError("Use shader", __FILE__, __LINE__); + + etc1Texture.Bind(); + //pvrTex.Bind(); + CheckGLError("Bind texture", __FILE__, __LINE__); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + CheckGLError("Draw plane", __FILE__, __LINE__); + + vaoQuad.Unbind(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisableVertexAttribArray(0); + CheckGLError("Draw", __FILE__, __LINE__); +} diff --git a/demos/ComprTex/Makefile.linux b/demos/ComprTex/Makefile.linux new file mode 100644 index 0000000..3aa5016 --- /dev/null +++ b/demos/ComprTex/Makefile.linux @@ -0,0 +1,17 @@ +TARGET = ComprTex +ENGINE_DIR = ../../nenuzhno-engine + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR) -I../../../../Libs/gl/glm/glm -I../../../../Libs/gl/glew-2.1.0/include +CPPFLAGS = -Wall -ggdb +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR) -lGL -lglfw3 -lX11 -lXrandr -lXinerama -lXcursor -lpthread -ldl -lGLEW -L../../../../Libs/gl/glfw-3.2.1_src/lib -L../../../../Libs/gl/glew-2.1.0/lib +SRCS = ComprTex.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/ComprTex/Makefile.mingw b/demos/ComprTex/Makefile.mingw new file mode 100644 index 0000000..57c9ea2 --- /dev/null +++ b/demos/ComprTex/Makefile.mingw @@ -0,0 +1,18 @@ +TARGET = ../../assets/ComprTex.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR)/src -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include +CPPFLAGS = -Wall -ggdb -m32 +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb -m32 +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR)/bin -lopengl32 -lglfw3 -lgdi32 -lglew32 -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +SRCS = ComprTex.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/SSR/Android.mk b/demos/SSR/Android.mk new file mode 100644 index 0000000..da88e58 --- /dev/null +++ b/demos/SSR/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../../nenuzhno-engine/ +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)game/IGame.cpp $(ENGINE_DIR)/cull/frustum.cpp \ + $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/gl_utils.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/vbo.cpp $(ENGINE_DIR)graphics/fbo.cpp \ + $(ENGINE_DIR)resource/mesh_loader.cpp \ + $(ENGINE_DIR)renderer/mesh.cpp $(ENGINE_DIR)renderer/camera.cpp \ + ssr.cpp +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/SSR/Makefile.mingw b/demos/SSR/Makefile.mingw new file mode 100644 index 0000000..eac2907 --- /dev/null +++ b/demos/SSR/Makefile.mingw @@ -0,0 +1,23 @@ +TARGET = ../../assets/ssr.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include -I$(LIBS_DIR)/gl/glm/glm -I$(ENGINE_DIR)/src +CPPFLAGS = -Wall -ggdb -m32 +LIBS = -lnenuzhno-engine -lglfw3 -lglew32 -lopengl32 -lgdi32 -L$(ENGINE_DIR)/bin -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +LDFLAGS = -static-libgcc -static-libstdc++ -m32 +SRCS = ssr.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(OBJS) $(TARGET) + +rebuild: clean all diff --git a/demos/SSR/ssr.cpp b/demos/SSR/ssr.cpp new file mode 100644 index 0000000..811b404 --- /dev/null +++ b/demos/SSR/ssr.cpp @@ -0,0 +1,252 @@ + +#include + +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/glsl_prog.h" +#include "graphics/ArrayBuffer.h" +#include "graphics/vao.h" +#include "renderer/mesh.h" +#include "graphics/texture.h" +#include "graphics/fbo.h" +#include "renderer/camera.h" +#include "game/IGame.h" + +class SSRGame : public IGame{ +public: + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "ssr"; + } +}; + +IGame *CreateGame(){ + return new SSRGame(); +} + +GLfloat vertices[] = +{ + 0.5f, 0.0f, 0.5f, 0,1,0, 1,1, + 0.5f, 0.0f, -0.5f, 0,1,0, 1,0, + -0.5f, 0.0f, -0.5f, 0,1,0, 0,0, + -0.5f, 0.0f, 0.5f, 0,1,0, 0,1 +}; + +int scrWidth = 0; +int scrHeight = 0; + +glslProg simpleProg; +glslProg texProg; +glslProg ssrProg; +int u_eyePos=-1; +Mesh *plane; +Mesh *cube; +VertexBufferObject *vboQuad; +VertexArrayObject vaoQuad; +VertexArrayObject vaoSO; +FrameBufferObject fbo1; +FrameBufferObject fboBack; +Texture testTex; +Camera camera; +glm::mat4 mvpMtx(1); + +void UnbindFBO() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, scrWidth, scrHeight); +} + +class SceneObject +{ +public: + Mesh *mesh; + glm::mat4 modelMtx; + + SceneObject(){} + SceneObject(Mesh *msh, glm::mat4 mtx) + { + mesh = msh; + modelMtx = mtx; + } + + void Draw(GLint u_mvpMtx) + { + mvpMtx = camera.projMtx * camera.viewMtx * modelMtx; + glUniformMatrix4fv(u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + + mesh->Bind(); + mesh->Draw(); + mesh->Unbind(); + } +}; + +SceneObject planeSO; +SceneObject cubeSO; + +void SSRGame::Created() +{ + simpleProg.CreateFromFile("simple", "simple"); + simpleProg.u_mvpMtx = simpleProg.GetUniformLoc("u_mvpMtx"); + if(texProg.CreateFromFile("generic", "col_tex")) + { + texProg.u_mvpMtx = texProg.GetUniformLoc("u_mvpMtx"); + texProg.u_color = texProg.GetUniformLoc("u_color"); + texProg.Use(); + glUniform4f(texProg.u_color, 1,1,1,1); + } + ssrProg.CreateFromFile("ssr", "ssr"); + ssrProg.u_mvpMtx = ssrProg.GetUniformLoc("u_mvpMtx"); + ssrProg.u_invModelMtx = ssrProg.GetUniformLoc("u_invMVPMtx"); + u_eyePos = ssrProg.GetUniformLoc("u_eyePos"); + GLint u_depthTex = ssrProg.GetUniformLoc("u_depthTex"); + ssrProg.Use(); + glUniform1i(u_depthTex, 1); + glUseProgram(0); + CheckGLError("Created shaders", __FILE__, __LINE__); + + plane = new MeshFBO_N3_T2(vertices, 4, GL_TRIANGLE_FAN); + cube = LoadMeshFile("cube", true); + vaoSO.Create(); + + float quadVerts[] ={ + 1,-1,1,0, + 1, 1,1,1, + -1, 1,0,1, + -1,-1,0,0 + }; + vboQuad = new VertexBufferObject(); + vboQuad->Create(); + vboQuad->Upload(4*4*4, quadVerts); + + vaoQuad.Create(); + vaoQuad.Bind(); + vboQuad->Bind(); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + vboQuad->Unbind(); + vaoQuad.Unbind(); + + CheckGLError("Created meshes", __FILE__, __LINE__); + + testTex.Create(4,4); + GLubyte testTexData[] = + { + 0,0,0, 255,255,255, 0,0,0, 255,255,255, + 255,255,255, 0,0,0, 255,255,255, 0,0,0, + 0,0,0, 255,255,255, 0,0,0, 255,255,255, + 255,255,255, 0,0,0, 255,255,255, 0,0,0 + }; + testTex.Upload(0, GL_RGB, testTexData); + + planeSO = SceneObject(plane,glm::mat4(1.0)); + cubeSO = SceneObject(cube,glm::scale(glm::translate(glm::mat4(1.0),glm::vec3(0,0.1,0)),glm::vec3(0.2))); + + camera.pos = glm::vec3(0,0.6f,1); + camera.rot = glm::vec3(30,0,0); + camera.UpdateView(); + + fbo1.Create(); + fbo1.CreateTexture(64, 64, GL_LINEAR); + fbo1.CreateDepthTexture(); + + fboBack.Create(); + fboBack.CreateTexture(256,256); + CheckGLError("Created fbo", __FILE__, __LINE__); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glEnable(GL_CULL_FACE); + CheckGLError("Created", __FILE__, __LINE__); +} + +void SSRGame::Changed(int w, int h) +{ + scrWidth = w; + scrHeight = h; + glViewport(0, 0, w, h); + float aspect = w/(float)h; + camera.UpdateProj(75.0f, aspect, 0.1f, 2.0f); + + fbo1.Resize(w,h); + CheckGLError("Changed", __FILE__, __LINE__); +} + +float a = 0; +void SSRGame::Draw() +{ + a+=0.02; + camera.pos = glm::vec3(glm::sin(a),0.6f,glm::cos(a)); + camera.rot = glm::vec3(30,glm::degrees(a),0); + camera.UpdateView(); + + fbo1.Bind(); + CheckGLError("Bind fbo", __FILE__, __LINE__); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + CheckGLError("Clear", __FILE__, __LINE__); + vaoSO.Bind(); + glEnableVertexAttribArray(0); + + glEnable(GL_DEPTH_TEST); + glEnableVertexAttribArray(2); + texProg.Use(); + testTex.Bind(); + cubeSO.Draw(texProg.u_mvpMtx); + glDisableVertexAttribArray(2); + CheckGLError("Draw cube", __FILE__, __LINE__); + + simpleProg.Use(); + planeSO.Draw(simpleProg.u_mvpMtx); + CheckGLError("Draw plane", __FILE__, __LINE__); + vaoSO.Unbind(); +#if 1 + glDisable(GL_DEPTH_TEST); + //UnbindFBO(); + fboBack.Bind(); + glClear(GL_COLOR_BUFFER_BIT); + + texProg.Use(); + glUniformMatrix4fv(texProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(glm::mat4(1.0f))); + fbo1.BindTexture(); + vaoQuad.Bind(); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + vaoQuad.Unbind(); + + ssrProg.Use(); + glm::mat4 invMVPMtx = glm::inverse(camera.projMtx * camera.viewMtx); + glUniformMatrix4fv(ssrProg.u_invModelMtx,1,GL_FALSE,glm::value_ptr(invMVPMtx)); + glUniform3fv(u_eyePos,1,&camera.pos.x); + glActiveTexture(GL_TEXTURE1); + fbo1.BindDepthTexture(); + glActiveTexture(GL_TEXTURE0); + fbo1.BindTexture(); + + vaoSO.Bind(); + glEnableVertexAttribArray(1); + planeSO.Draw(ssrProg.u_mvpMtx); + glDisableVertexAttribArray(1); + + UnbindFBO(); + glClear(GL_COLOR_BUFFER_BIT); + texProg.Use(); + glUniformMatrix4fv(texProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(glm::mat4(1.0f))); + fboBack.BindTexture(); + vboQuad->Bind(); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + glEnableVertexAttribArray(2); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + glDisableVertexAttribArray(2); + vboQuad->Unbind(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisableVertexAttribArray(0); + vaoSO.Unbind(); +#endif + CheckGLError("Draw", __FILE__, __LINE__); +} diff --git a/demos/StarFleet/Android.mk b/demos/StarFleet/Android.mk new file mode 100644 index 0000000..7f3d3ff --- /dev/null +++ b/demos/StarFleet/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../nenuzhno-engine/ +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)game/IGame.cpp $(ENGINE_DIR)/cull/frustum.cpp $(ENGINE_DIR)/cull/BoundingBox.cpp $(ENGINE_DIR)scene/Scene.cpp \ + $(ENGINE_DIR)graphics/gl_utils.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/vbo.cpp $(ENGINE_DIR)graphics/fbo.cpp \ + $(ENGINE_DIR)renderer/renderer.cpp $(ENGINE_DIR)renderer/camera.cpp $(ENGINE_DIR)renderer/mesh.cpp $(ENGINE_DIR)renderer/Model.cpp $(ENGINE_DIR)renderer/font.cpp \ + $(ENGINE_DIR)resource/ResourceManager.cpp $(ENGINE_DIR)resource/vtf_loader.cpp $(ENGINE_DIR)resource/dds_loader.cpp $(ENGINE_DIR)resource/mesh_loader.cpp $(ENGINE_DIR)resource/nmf_loader.cpp \ + star-fleet.cpp input.cpp Ship.cpp Explosion.cpp Projectile.cpp +# $(ENGINE_DIR)renderer/vtf_loader.cpp \ +# renderer/progs_manager.cpp renderer/material.cpp renderer/render_list.cpp +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL +LOCAL_STATIC_LIBRARIES = + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/StarFleet/Explosion.cpp b/demos/StarFleet/Explosion.cpp new file mode 100644 index 0000000..940b12d --- /dev/null +++ b/demos/StarFleet/Explosion.cpp @@ -0,0 +1,25 @@ + +#include +#include "entity.h" + +Explosion::Explosion(glm::vec3 pos, float aSize): Entity(){ + dynamic = true; + type=eExpl; + life=0; + position=pos; + size=aSize; + + modelMtx = glm::translate(glm::mat4(1),position); + displayMtx = glm::scale(modelMtx,glm::vec3(size)); +} + +void Explosion::Update(float deltaTime){ + life+=deltaTime; + + if(life>1.0f){ + remove = true; + return; + } + displayMtx = glm::scale(modelMtx,glm::vec3(size*pow((1.0-life),3.0))); +} + diff --git a/demos/StarFleet/Makefile.mingw b/demos/StarFleet/Makefile.mingw new file mode 100644 index 0000000..42c8d0f --- /dev/null +++ b/demos/StarFleet/Makefile.mingw @@ -0,0 +1,29 @@ + +ENGINE_DIR = ../.. +TARGET := $(ENGINE_DIR)/assets/StarFleet.exe +LIBS_DIR = ../../../../../Libs +all: $(TARGET) + +INCLUDES = -I. -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include -I$(ENGINE_DIR)/src +CPPFLAGS = -Wall -ggdb -m32 +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb -m32 +LIBS = -lnenuzhno-engine -lglfw3 -lglew32 -lopengl32 -lgdi32 -L$(ENGINE_DIR)/bin -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +SRCS = star-fleet.cpp input.cpp Ship.cpp Explosion.cpp Projectile.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) + +clean: + $(RM) $(OBJS) $(TARGET) + +rebuild: clean all + +obj/star-fleet.o: entity.h +obj/input.o: entity.h +obj/Ship.o: entity.h +obj/Explosion.o: entity.h +obj/Projectile.o: entity.h diff --git a/demos/StarFleet/Projectile.cpp b/demos/StarFleet/Projectile.cpp new file mode 100644 index 0000000..468097b --- /dev/null +++ b/demos/StarFleet/Projectile.cpp @@ -0,0 +1,34 @@ + +#include +#include "entity.h" + +void TraceLine(Projectile *proj, float deltaTime); + +Projectile::Projectile(Entity *s,float aDamage): Entity() +{ + dynamic = true; + type = eProj; + life = 0; + shooter = s; + damage = aDamage; +} +void Projectile::Update(float deltaTime) +{ + if(remove) + return; + + life+=deltaTime; + if(life>4) + { + remove = true; + CreateExplosion(this,0.5f); + return; + } + modelMtx = glm::translate(glm::mat4(1),position); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.y),glm::vec3(0,1,0)); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.x),glm::vec3(1,0,0)); + position += /*glm::mat3(modelMtx)**/velocity*deltaTime; + displayMtx = glm::scale(modelMtx,glm::vec3(0.02,0.02,0.1)); + TraceLine(this,deltaTime); +} + diff --git a/demos/StarFleet/Ship.cpp b/demos/StarFleet/Ship.cpp new file mode 100644 index 0000000..b217c74 --- /dev/null +++ b/demos/StarFleet/Ship.cpp @@ -0,0 +1,126 @@ + +#include +#include +#include "entity.h" +#include "log.h" +#include "renderer/renderer.h" + +Ship::Ship(): Entity(),gun(10,6.5f,20,0.35f,0.05f){ + dynamic = true; + type = eShip; + state = eShipState_Idle; + lastShot = 0; + hp = 100; + t = 0; + target = 0; + targetPos = glm::vec3(0); + isDead = false; + bbox = BoundingBox(glm::vec3(-0.6f,-0.5f,-0.8f),glm::vec3(0.6f,0.5f,0.8f)); +} + +void Ship::Update(float deltaTime){ + lastShot += deltaTime; + t -= deltaTime; + if(hp <= 0.0f){ + //isDead = true; + return; + } + modelMtx = glm::translate(glm::mat4(1),position); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.y),glm::vec3(0,1,0)); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.x),glm::vec3(1,0,0)); + if(state != eShipState_Idle){ + glm::vec3 forward = glm::normalize(glm::mat3(modelMtx)[2]); + position += glm::mat3(modelMtx)*velocity*deltaTime; + glm::vec3 da; + if(target&&target->isDead) + target=0; + //todo find new target + if(target){ + toEnemy = target->position - position; + float dist = glm::length(toEnemy); + if(dist > 2.0) + velocity = glm::vec3(0,0,2); + else + velocity = glm::vec3(0,0,1); + //advance aiming + toEnemy = glm::normalize(target->position+(normalize(glm::vec3(target->modelMtx[2]))*target->velocity.z*(dist/gun.speed))-position); + if(state==eShipState_Attack&&lastShot>gun.rate){ + if(dist0.95){ + Shoot(); + lastShot=0; + } + } + newAngles.x = glm::degrees(glm::asin(-toEnemy.y)); + newAngles.y = glm::degrees(glm::atan(toEnemy.x,toEnemy.z)); + }else{ + if(t<0){ + t = glm::linearRand(2.0f,5.0f); + //newAngles = glm::sphericalRand(180.0f); + targetPos=glm::ballRand(50.0f); + glm::vec3 newDir = normalize(targetPos-position); + newAngles.x = glm::degrees(glm::asin(-newDir.y)); + newAngles.y = glm::degrees(glm::atan(newDir.x,newDir.z)); + newAngles.z = 0; + } + } + //angles = newAngles; + da = newAngles-angles; + /*if(da.y>180) + da.y-=180; + if(da.y<-180) + da.y+=180;*/ + if(abs(da.y)>180){ + da.y-= ((int)da.y)/180*180; + } + da = glm::clamp(da,glm::vec3(-5.0f),glm::vec3(5.0f)); + //dy = glm::clamp(y-angles.y,-5.0f,+5.0f); + angles += da*deltaTime*20.0f; + //angles.y += dy*deltaTime*20.0f; + } + displayMtx = glm::rotate(glm::scale(modelMtx,glm::vec3(0.1)),glm::radians(-90.0f),glm::vec3(0,1,0)); +} + +void Ship::Shoot() +{ + //pew + CreateProjectile(this,(toEnemy+glm::sphericalRand(gun.spread))*gun.speed,gun.damage); +} + +void Ship::Hit(float d) +{ + hp-=d; + if(hp<=0.0) + { + //Log("Ship %p dead\n",this); + CreateExplosion(this,2.0f); + //isDead = true; + //todo respawn + hp = 100; + position = glm::ballRand(12.0f);//glm::vec3(0.0f); + angles = glm::vec3(0.0f); + } +} + +Asteroid::Asteroid(glm::vec3 pos,float s){ + dynamic=true; + position = pos; + size = s; + ang = true; + modelMtx = glm::translate(glm::mat4(size),pos); + displayMtx = modelMtx; +} + +using glm::abs; + +void Asteroid::Update(float deltaTime){ + position += velocity*deltaTime; + if(abs(position.x)>300||abs(position.y)>300||abs(position.z)>300){ + position = glm::ballRand(250.0f); + } + angles.x += ang*deltaTime; + + modelMtx = glm::translate(glm::mat4(size),position); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.x),glm::vec3(0.8,0.3,0.5)); + displayMtx = modelMtx; +} + diff --git a/demos/StarFleet/TODO.txt b/demos/StarFleet/TODO.txt new file mode 100644 index 0000000..c62f6d1 --- /dev/null +++ b/demos/StarFleet/TODO.txt @@ -0,0 +1,20 @@ +UI + change font+ + pause menu+ + settings + HUD- +Gameplay + controll + better AI + more weapons + more ships +Graphics + better explosion + better models + environment + lighting + rework renderer- +Other + fix collision+ + sounds + diff --git a/demos/StarFleet/entity.h b/demos/StarFleet/entity.h new file mode 100644 index 0000000..7d21d60 --- /dev/null +++ b/demos/StarFleet/entity.h @@ -0,0 +1,122 @@ + +#pragma once + +#include +#include +#include "cull/Boundingbox.h" +#include "scene/Scene.h" + +class IRenderer; +class Entity; +void CreateProjectile(Entity *shooter,glm::vec3 dir,float damage); +void CreateExplosion(Entity *owner,float size); + +enum entType{ + eNone, + eExpl, + eProj, + eShip +}; + +class Entity: public SceneObject +{ +public: + glm::mat4 displayMtx; + glm::vec3 velocity; + glm::vec3 position; + glm::vec3 angles; + bool remove; + int type; + + Entity(){ + type=eNone; + remove=false; + modelMtx=glm::mat4(1.0f); + displayMtx=glm::mat4(1.0f); + } + virtual ~Entity(){} + + virtual void Update(float deltaTime){} +}; + +class Asteroid: public Entity{ +public: + Asteroid(glm::vec3 pos, float size); + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); + + float size; + float ang; +};//in ship.cpp + +class Explosion: public Entity +{ +public: + Explosion(glm::vec3 pos, float aSize); + float life; + float size; + + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); +}; + +class Projectile: public Entity +{ +public: + float life; + float damage; + Entity *shooter; + + Projectile(Entity *s,float damage); + ~Projectile(){} + + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); +}; + +struct shipGun_t{ + float range; + float speed; + float damage; + float rate; + float spread; + + shipGun_t(float aRange, float aSpeed,float aDamage,float aRate, float aSpread){ + range = aRange; + speed = aSpeed; + damage = aDamage; + rate = aRate; + spread = aSpread; + } +}; + +enum eShipState +{ + eShipState_Idle, + eShipState_Pursue, + eShipState_Attack, + //eShipState_LastEnum +}; +class Ship: public Entity +{ +public: + eShipState state; + float lastShot; + float hp; + float t; + Ship *target; + glm::vec3 targetPos; + glm::vec3 toEnemy; + bool isDead; + glm::vec3 newAngles; + BoundingBox bbox; + shipGun_t gun; + + Ship(); + ~Ship(){} + + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); + void Shoot(); + void Hit(float d); +}; diff --git a/demos/StarFleet/input.cpp b/demos/StarFleet/input.cpp new file mode 100644 index 0000000..bbcb706 --- /dev/null +++ b/demos/StarFleet/input.cpp @@ -0,0 +1,70 @@ + +#include "button.h" +#include "log.h" +#include "star-fleet.h" + +extern float aspect; +extern Button bPause; +extern Button bResume; + +void Button::Update(){ + if(!active || !func) + return; + if(pressed){ + pressed = false; + func(); + } +} + +Button::Button(float nx, float ny, float nw, float nh, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(0),active(true),pressed(false) +{ + if(adjust){ + if(aspect>1) + h*=aspect; + else + w/=aspect; + } + //Log("Created button %f %f %f %f\n",x,y,w,h); +} + +Button::Button(float nx, float ny, float nw, float nh, const char *t, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(t),active(true),pressed(false) +{ + if(adjust) + w/=aspect; + + //Log("Created text button %f %f %f %f %s\n",x,y,w,h,t); +} +/* +bool Button::Hit(float tx, float ty) +{ + if(!active) + return false; + if(tx>x+w||txy+h||ty +void StarFleetGame::OnKey(int key, int scancode, int action, int mods) +{ + if(action==GLFW_PRESS){ + if(key==GLFW_KEY_ESCAPE){ + if(gameState==eGameState_Play) + bPause.pressed = true; + else if(gameState==eGameState_Pause) + bResume.pressed = true; + } + } +} +#else +void StarFleetGame::OnKey(int key, int scancode, int action, int mods) +{ + +} +#endif diff --git a/demos/StarFleet/star-fleet.cpp b/demos/StarFleet/star-fleet.cpp new file mode 100644 index 0000000..c367eb9 --- /dev/null +++ b/demos/StarFleet/star-fleet.cpp @@ -0,0 +1,528 @@ + +#include +#include +#include +using namespace std; +#include +#include +#include + +#include "log.h" +#include "engine.h" +#include "game/IGame.h" +#include "resource/ResourceManager.h" +#include "renderer/renderer.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/glsl_prog.h" +#include "renderer/mesh.h" +#include "graphics/texture.h" +#include "renderer/camera.h" +#include "renderer/font.h" +#include "renderer/Material.h" +#include "button.h" + +#include "entity.h" + +#include "star-fleet.h" + +StarFleetGame *gGame=0; + +IGame *CreateGame(){ + gGame = new StarFleetGame(); + return gGame; +} + +eGameState gameState = eGameState_Menu; + +glslProg starboxProg; + +Texture starboxTex; + +Model *modelShip; +Model *modelCube; +//Mesh *shipMesh; +Texture shipTex; +TexMaterial matLight(&shipTex,false); +#define SHIP_COUNT 10 +Ship *ships[SHIP_COUNT]; +vector entities; + +//menu buttons +std::vector buttons; +//start +Button bNewGame; +Button bQuit; +//game +Button bPause; +//pause +Button bResume; + +void NewGame(); +void Quit(); +void Pause(); +void Resume(); + +void UpdateButtons() +{ + bNewGame = Button(0.1,0.3,0.5,0.1,"New game"); + bQuit = Button(0.1,0.6,0.5,0.1,"Quit"); + bPause = Button(0.75,0.02,0.2,0.1,"[=] Pause"); + bResume = Button(0.1,0.3,0.5,0.1,"Resume"); + bNewGame.func=NewGame; + bPause.func=Pause; + bQuit.func=Quit; + bResume.func=Resume; +} + +void ChangeState(eGameState s){ + gameState = s; + + for(uint32_t i=0; iactive = false; + } + + switch(s){ + case eGameState_Menu: + bNewGame.active = true; + bQuit.active = true; + break; + case eGameState_Play: + bPause.active = true; + break; + case eGameState_Pause: + bQuit.active = true; + bResume.active = true; + break; + } +} + +void StartMenu() +{ + buttons.clear(); + buttons.push_back(&bNewGame); + buttons.push_back(&bPause); + buttons.push_back(&bQuit); + buttons.push_back(&bResume); + + //TODO remove! + gGame->renderer->AddButton(&bNewGame); + gGame->renderer->AddButton(&bPause); + gGame->renderer->AddButton(&bQuit); + gGame->renderer->AddButton(&bResume); + + //UpdateButtons(); //in Changed() + + ChangeState(eGameState_Menu); +} + +void NewGame() +{ + ChangeState(eGameState_Play); + + if(!entities.empty()) + EngineError("Game already started"); + srand(time(0)); + for(int i=0; iposition = glm::vec3(glm::sin(i*0.85f)*5.0,0,-i*1.5f); + ship->velocity = glm::vec3(0,0,2.0f);//glm::sphericalRand(0.2f); + ship->angles = glm::sphericalRand(180.0f); + ship->state = (eShipState)((i%2)+1); + if(i%2) + ship->target = ships[i-1]; + entities.push_back(ship); + ships[i] = ship; + gGame->scene->AddObject(ship); + } + ships[1]->gun = shipGun_t(16,20.0f,4,0.04f,0.04f); + //ships[1]->target = entities[2]; + + gGame->camera.pos = glm::vec3(0,1.6f,2); + gGame->camera.rot = glm::vec3(30,0,0); + gGame->camera.UpdateView(); + + for(int i=0; i<1000; i++){ + Asteroid *obj = new Asteroid(glm::ballRand(250.0f),glm::linearRand(0.05f,5.0f)); + if(i<600){ + obj->velocity = glm::ballRand(20.0f); + obj->ang = glm::linearRand(-40,40); + } + gGame->scene->AddObject(obj); + } +} + +void Quit(){ + exit(0); +} + +void Pause(){ + ChangeState(eGameState_Pause); +} + +void Resume(){ + ChangeState(eGameState_Play); +} + +void StarFleetGame::Created() +{ + Log("Init nenuzhno engine. StarFleet\n"); + + resMan = new ResourceManager(); + resMan->Init(); + renderer = CreateRenderer(); + renderer->Init(RENDERER_GUI|RENDERER_LIGHT,resMan); + + scene = new Scene(); + renderer->SetScene(scene); + + starboxProg.CreateFromFile("skybox","skybox"); + starboxProg.u_mvpMtx = starboxProg.GetUniformLoc("u_mvpMtx"); + glUseProgram(0); + CheckGLError("Created shaders", __FILE__, __LINE__); + + GLubyte testTexData[] = + { + 63,63, 255,255, 31,31, 255,255, + 63,63, 255,255, 31,31, 255,255, + 255,255, 63,63, 255,255, 31,31, + 255,255, 63,63, 255,255, 31,31, + 31,31, 255,255, 63,63, 255,255, + 31,31, 255,255, 63,63, 255,255, + 255,255, 31,31, 255,255, 63,63, + 255,255, 31,31, 255,255, 63,63 + }; + shipTex.Create(8,8); + shipTex.SetFilter(GL_LINEAR, GL_LINEAR); + shipTex.Upload(0, GL_LUMINANCE, testTexData); + + resMan->AddMaterial("light",&matLight); + + GLubyte *starboxData = new GLubyte[64*64]; + memset(starboxData,0,64*64); + starboxData[75]=255; + starboxData[130]=255; + starboxData[406]=255; + starboxData[625]=255; + starboxData[1307]=255; + starboxData[3506]=255; + starboxData[4002]=255; + starboxData[2383]=255; + starboxTex.target = GL_TEXTURE_CUBE_MAP; + starboxTex.Create(64,64); + starboxTex.SetFilter(GL_LINEAR, GL_LINEAR); + for(int i=0; i<6; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_LUMINANCE, 64, 64, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, starboxData); + } + delete[] starboxData; + //TODO: Move to StartGame? + scene->skyBox = &starboxTex; + + CheckGLError("Created texture", __FILE__, __LINE__); + + //shipMesh = new MeshFBO_N3_T2(vertices, 4, GL_TRIANGLE_FAN); + //shipMesh = LoadMeshFile("sship1", true); + + modelCube = resMan->GetModel("cube.nmf"); + modelShip = resMan->GetModel("sship1.nmf"); + CheckGLError("Created meshes", __FILE__, __LINE__); + + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, 0); + glClearColor(0, 0, 0, 1); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + CheckGLError("Created", __FILE__, __LINE__); + + StartMenu(); + + oldTime=GetTime(); +} + +float aspect;//for input +void StarFleetGame::Changed(int w, int h) +{ + renderer->Resize(w,h); + aspect = renderer->aspect; + camera.UpdateProj(85.0f, renderer->aspect, 0.1f, 1500.0f); + renderer->SetCamera(&camera); + UpdateButtons(); + ChangeState(gameState); +} + +void StarFleetGame::Update() +{ + double startTime = GetTime(); + deltaTime = float(startTime-oldTime); + oldTime = startTime; + + for(uint32_t i=0; iUpdate(); + } + + if(gameState==eGameState_Play){ + scene->Update(deltaTime); + + //for(vector::iterator it=entities.begin();itUpdate(deltaTime); + if(ent->remove){ + entities.erase(entities.begin()+i); + gGame->scene->RemoveObject(ent); + delete ent; + i--; + } + } + camera.viewMtx = glm::lookAt( + //glm::vec3(3.2f,0,0), + entities[1]->position+glm::mat3(entities[1]->modelMtx)*glm::vec3(0.5,1.3,-1.6)+glm::vec3(0,0.2,0), + entities[1]->position+glm::mat3(entities[1]->modelMtx)*glm::vec3(0.4,1.0,0.8), + glm::vec3(0,1,0)); + +// camera.UpdateView(); + } +} + +void Ship::Draw(IRenderer *r){ + r->DrawModel(modelShip, displayMtx); +} + +void Projectile::Draw(IRenderer *r){ + r->SetColor(1,0,0,1); + r->DrawModel(modelCube, displayMtx); +} + +void Explosion::Draw(IRenderer *r){ + r->SetColor(1,0.5,0,1); + r->DrawModel(modelCube, displayMtx); +} + +void Asteroid::Draw(IRenderer *r){ + r->SetColor(0.7,0.7,0.7,1); + r->DrawModel(modelCube, displayMtx); +} + +/* +void DrawText(const char *t,float x,float y,float s) +{ + //glEnable(GL_BLEND); + //glBlendFunc(1,1); + testFont.Print(t,x,y/renderer->aspect,s); + //glDisable(GL_BLEND); +}*/ +/* +void DrawRect(float x, float y, float w, float h) +{ + float lx=x; + float rx=(x+w); + float dy=(y+h); + float verts[] = { + lx, y, 0,0, + lx, dy, 0,1, + rx, dy, 1,1, + rx, y, 1,0 + }; + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, verts); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 16, verts+2); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} +*/ +/* +void StarFleetGame::DrawButton(Button *b) +{ + if(b->text){ + //testFont.Print(b->text,b->x*aspect+0.01,1-b->y-0.05,0.5); + //float x = b->x*2.0f-1.0f; + //float y = 1-(b->y*2.0f-1.0f); + renderer->DrawText(b->text,b->x+0.01,b->y-0.05f,0.5); + }//else + { + //DrawRect(b->x,b->y,b->w,b->h); + } +}*/ + +void StarFleetGame::Draw() +{ + Update(); + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + CheckGLError("Clear", __FILE__, __LINE__); + + glEnableVertexAttribArray(0); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + renderer->Draw(); +#if 0 + glEnableVertexAttribArray(2); + texProg.Use(); + glUniform4f(texProg.u_color,1,1,1,1); + if(gameState==eGameState_Play||gameState==eGameState_Pause){ + shipMesh->Bind(); + shipTex.Bind(); + for(int i=0;iisDead) + continue; + mvpMtx = camera.projMtx * camera.viewMtx * entities[i]->displayMtx; + glUniformMatrix4fv(texProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + shipMesh->Draw(); + } + cubeMesh->Bind(); + //proj + texWhite.Bind(); + glUniform4f(texProg.u_color,1,0,0,1); + for(uint32_t i=SHIP_COUNT;itype!=eProj) + continue; + mvpMtx = camera.projMtx * camera.viewMtx * entities[i]->displayMtx; + glUniformMatrix4fv(texProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + cubeMesh->Draw(); + } + //expl + glUniform4f(texProg.u_color,1,0.5f,0,1); + for(uint32_t i=SHIP_COUNT;itype!=eExpl) + continue; + mvpMtx = camera.projMtx * camera.viewMtx * entities[i]->displayMtx; + glUniformMatrix4fv(texProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + cubeMesh->Draw(); + } + cubeMesh->Unbind(); + glUniform4f(texProg.u_color,1,1,1,1); + } +#endif + renderer->SetColor(1,1,1,1); + //2D + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + //glm::mat4 mvpMtx = glm::scale(glm::mat4(1.0),glm::vec3(1.0f,renderer->aspect,1.0f)); + //glUniformMatrix4fv(texProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + if(gameState==eGameState_Play){ + //glUniform4f(texProg.u_color,1,1,1,1); + renderer->Set2DMode(); + char str[64]; + snprintf(str,64,"hp: %.0f",ships[1]->hp); + renderer->DrawText(str,0,0.1,0.48); + + snprintf(str,64,"ents: %d",entities.size()); + renderer->DrawText(str,0.4,0.1,0.48); + + renderer->SetColor(0,1,0,1); + for(int i=0;iisDead) + continue; + glm::mat4 tmtx = camera.projMtx * camera.viewMtx * entities[i]->modelMtx; + glm::vec4 p(0,0,0,1); + p=tmtx*p; + p/=p.w; + p.y = (-p.y/renderer->aspect*0.5+0.5); + p.x = p.x*0.5+0.5; + if(p.z>0&&p.z<1){ + //DrawRect(p.x-0.15,p.y+0.2,0.3*ships[i]->hp*0.01,0.02); + renderer->DrawRect(p.x-0.15,p.y-0.2,0.3*ships[i]->hp*0.01,0.02); + } + } + } + renderer->SetColor(1,1,1,1); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(2); + + CheckGLError("Draw", __FILE__, __LINE__); +} + +void StarFleetGame::OnTouch(float x, float y, int a, int tf) +{ + float tx = x/renderer->width; + float ty = y/renderer->height; + if(a==0){ + for(uint32_t i=0; iactive) + buttons[i]->Hit(tx,ty); + } + } +} + +bool Button::SetUniform(int loc) +{ + glUniform4f(loc, x*2.0f-(1-w), -(y*2.0f-(1-h)), w, h); + return true; +} + +class Projectile; +void TraceLine(Projectile *proj, float deltaTime); + +void CreateExplosion(Entity *owner,float size){ + Explosion *expl = new Explosion(owner->position,size); + expl->angles = owner->angles; + entities.push_back(expl); + gGame->scene->AddObject(expl); +} + +void CreateProjectile(Entity *shooter, glm::vec3 dir,float damage) +{ + Projectile *proj = new Projectile(shooter,damage); + proj->position = shooter->position; + proj->angles = shooter->angles; + //proj->velocity = glm::normalize(shooter->velocity)*2.0f; + proj->velocity = dir; + + entities.push_back(proj); + gGame->scene->AddObject(proj); + //Log("CreateProjectile entities.size() %d\n",entities.size()); +} + +void TraceLine(Projectile *proj, float deltaTime) +{ + for(int i=0;iisDead||ent==proj->shooter) + continue; + + glm::mat4 invmodelMtx = glm::inverse(ent->modelMtx); + glm::vec3 s = glm::vec3(invmodelMtx*(glm::vec4(proj->position,1.0f))); + glm::vec3 d = glm::vec3(invmodelMtx*(glm::vec4(proj->velocity*deltaTime,1.0f)));//proj->modelMtx[2]; +#if 0 + glm::vec3 p = glm::vec3(0.0f); + glm::vec3 e = glm::vec3(0.5f); + float hl = glm::length(d)*0.5f; + glm::vec3 m = s+d*0.5f; + glm::vec3 t = p-m; + + if( (glm::abs(t.x)>e.x+hl+glm::abs(d.x)) || + (glm::abs(t.y)>e.y+hl+glm::abs(d.y)) || + (glm::abs(t.z)>e.z+hl+glm::abs(d.x)) ) + continue; + + float r = e.y*glm::abs(d.z)+e.z*glm::abs(d.y); + if(glm::abs(t.y*d.z-t.z*d.y)>r) + continue; + + r = e.x*glm::abs(d.z)+e.z*glm::abs(d.x); + if(glm::abs(t.z*d.x-t.x*d.z)>r) + continue; + + r = e.x*glm::abs(d.y)+e.y*glm::abs(d.x); + if(glm::abs(t.x*d.y-t.y*d.x)>r) + continue; +#endif + if(!ent->bbox.Intersect(s,d)) + continue; + + //Log("Ship %p hit %p\n",proj->shooter,ent); + CreateExplosion(proj,0.2f); + proj->remove = true; + ent->Hit(proj->damage); + return; + } +} + diff --git a/demos/StarFleet/star-fleet.h b/demos/StarFleet/star-fleet.h new file mode 100644 index 0000000..5eb5b9d --- /dev/null +++ b/demos/StarFleet/star-fleet.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "game/IGame.h" +#include "renderer/camera.h" + +class ResourceManager; +class IRenderer; +class Scene; + +class StarFleetGame: public IGame{ +public: + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "star-fleet"; + } + + void OnKey(int key, int scancode, int action, int mods); + void OnTouch(float tx, float ty, int ta, int tf); + + ResourceManager *resMan; + IRenderer *renderer; + Camera camera; + Scene *scene; + + double oldTime; + float deltaTime; + + void Update(); + void DrawButton(Button *b); +}; + +enum eGameState +{ + eGameState_Menu, + eGameState_Play, + eGameState_Pause +}; +extern eGameState gameState; diff --git a/demos/VolLight/Android.mk b/demos/VolLight/Android.mk new file mode 100644 index 0000000..4b24ae1 --- /dev/null +++ b/demos/VolLight/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../../nenuzhno-engine/ + +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)system/config.cpp $(ENGINE_DIR)game/IGame.cpp \ + $(ENGINE_DIR)renderer/renderer.cpp $(ENGINE_DIR)renderer/LightingForward.cpp $(ENGINE_DIR)renderer/font.cpp $(ENGINE_DIR)renderer/mesh.cpp $(ENGINE_DIR)renderer/Model.cpp $(ENGINE_DIR)renderer/camera.cpp \ + $(ENGINE_DIR)graphics/vbo.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/fbo.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/gl_utils.cpp \ + $(ENGINE_DIR)scene/Scene.cpp $(ENGINE_DIR)cull/BoundingBox.cpp $(ENGINE_DIR)cull/Frustum.cpp \ + $(ENGINE_DIR)resource/ResourceManager.cpp $(ENGINE_DIR)resource/vtf_loader.cpp $(ENGINE_DIR)resource/dds_loader.cpp $(ENGINE_DIR)resource/nmf_loader.cpp $(ENGINE_DIR)resource/mesh_loader.cpp \ + main.cpp Volumetrics.cpp +# $(ENGINE_DIR)graphics/gl_ext.cpp $(ENGINE_DIR)graphics/vao.cpp + +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/VolLight/Makefile.mingw b/demos/VolLight/Makefile.mingw new file mode 100644 index 0000000..183f49b --- /dev/null +++ b/demos/VolLight/Makefile.mingw @@ -0,0 +1,18 @@ +TARGET = ../../assets/VolLight.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR)/src -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include +CPPFLAGS = -Wall -ggdb -m32 +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb -m32 +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR)/bin -lopengl32 -lglfw3 -lglew32 -lgdi32 -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw -L$(LIBS_DIR)/gl/glew-2.0.0/lib +SRCS = main.cpp Volumetrics.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/VolLight/Volumetrics.cpp b/demos/VolLight/Volumetrics.cpp new file mode 100644 index 0000000..dd8bc44 --- /dev/null +++ b/demos/VolLight/Volumetrics.cpp @@ -0,0 +1,66 @@ + +#include "Volumetrics.h" + +#include "scene/Scene.h" +#include "renderer/renderer.h" + +#include +#include +#include +using glm::vec4; +using glm::mat4; + +Volumetrics::Volumetrics(){ + lightObj = 0; +} + +void Volumetrics::Init(Scene *sc,vec3 pos,vec3 target,float fov, float aspect,float aNear,float aFar,int lightRes) +{ + lightObj = new LightObject(ePoint,pos,vec3(1),aFar); + sc->AddLight(lightObj); + + camera = Camera(); + camera.LookAt(lightObj->pos,target,vec3(0,1,0)); + camera.UpdateProj(fov,aspect,aNear,aFar); + camera.UpdateFrustum(); + depthFBO.Create(); + depthFBO.CreateDepthTexture(lightRes,lightRes); + FrameBufferObject::Unbind(); +} + +void Volumetrics::AddToDepth(IRenderer *rend){ + mat4 mtx = scale(glm::inverse(camera.projMtx*camera.viewMtx),vec3(2)); + rend->SetModelMtx(mtx); + rend->DrawCube(); +} + +void Volumetrics::Prepare(IRenderer *rend){ + depthFBO.Bind(); + depthFBO.SetupViewport(); + depthFBO.Clear(2); + rend->RenderDepth(&camera); + depthFBO.Unbind(); + rend->ResetViewport(); +} + +void Volumetrics::Draw(IRenderer *rend,int u_lightMtx,int u_invVPMtx, int u_lightPosSize){ + mat4 lightMtx = camera.projMtx*camera.viewMtx; + //progVolLight.UniformMat4(u_lightMtx,lightMtx); + glUniformMatrix4fv(u_lightMtx,1,false,glm::value_ptr(lightMtx)); + lightMtx = scale(glm::inverse(lightMtx),vec3(2)); + rend->SetModelMtx(lightMtx); + //progVolLight.UniformMat4(u_invVPMtx,glm::inverse(rend->vpMtx)); + glUniformMatrix4fv(u_invVPMtx,1,false,glm::value_ptr(glm::inverse(rend->vpMtx))); + glUniform4f(u_lightPosSize,lightObj->pos.x,lightObj->pos.y,lightObj->pos.z,lightObj->radius); + glActiveTexture(GL_TEXTURE1); + depthFBO.BindDepthTexture(); + glActiveTexture(GL_TEXTURE2); + rend->GetFBO()->BindDepthTexture(); + glActiveTexture(GL_TEXTURE0); + + rend->DrawCube(); +} + +bool Volumetrics::CheckPoint(glm::vec3 pos){ + return camera.frustum.Contains(vec4(pos,0.02)); +} \ No newline at end of file diff --git a/demos/VolLight/Volumetrics.h b/demos/VolLight/Volumetrics.h new file mode 100644 index 0000000..91754fc --- /dev/null +++ b/demos/VolLight/Volumetrics.h @@ -0,0 +1,24 @@ + +#pragma once + +class Scene; +class IRenderer; +class LightObject; +#include "renderer/camera.h" +#include "graphics/fbo.h" + +#include +using glm::vec3; + +class Volumetrics{ +public: + Volumetrics(); + void Init(Scene *sc,vec3 pos,vec3 target,float fov, float aspect,float aNear,float aFar,int lightRes); + void AddToDepth(IRenderer *rend); + void Prepare(IRenderer *rend); + void Draw(IRenderer *rend,int u_lightMtx,int u_invVPMtx, int u_lightPosSize); + bool CheckPoint(vec3 pos); + LightObject *lightObj; + Camera camera; + FrameBufferObject depthFBO; +}; diff --git a/demos/VolLight/main.cpp b/demos/VolLight/main.cpp new file mode 100644 index 0000000..a784d9c --- /dev/null +++ b/demos/VolLight/main.cpp @@ -0,0 +1,283 @@ + +#include + +#include "graphics/platform_gl.h" +#include "log.h" +#include "engine.h" +#include "button.h" +#include "game/IGame.h" +#include "system/config.h" +#include "resource/ResourceManager.h" +#include "renderer/renderer.h" +#include "renderer/mesh.h" +#include "renderer/material.h" +#include "renderer/model.h" +#include "graphics/texture.h" +#include "graphics/fbo.h" +#include "graphics/glsl_prog.h" + +#include "Volumetrics.h" + +#include +#include +#include +#include +#include +#include +using glm::vec2; +using glm::vec3; +using glm::vec4; +using glm::linearRand; +using glm::clamp; +using glm::abs; +using glm::normalize; +using glm::translate; +using glm::scale; + +class VolLightGame: public IGame{ +public: + VolLightGame(){} + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "vol_light"; + } + void OnTouch(float tx, float ty, int ta, int tf); + void OnKey(int key, int scancode, int action, int mods); + + IRenderer *rend; + ResourceManager *resMan; + ConfigFile cfg; + Camera cam; + Scene scene; + + Texture texWhite; + TexMaterial *matWhite; + + Volumetrics lightVol; + FrameBufferObject volFBO; + float volScale; + glslProg progVolLight; + int u_lightMtx; + int u_invVPMtx; + int u_lightPosSize; + glslProg progVolLightIn; + int u_lightMtxIn; + int u_invVPMtxIn; + int u_lightPosSizeIn; + + Joystick joyL; + KeyJoystick joyM; + int frame; + + double oldTime; +}; + +IGame *CreateGame(){ + return new VolLightGame(); +} + +void VolLightGame::Created(){ + Log("VolLight test Created()\n"); + resMan = new ResourceManager(); + resMan->Init(); + rend = CreateRenderer(); + rend->Init(RENDERER_GUI|RENDERER_LIGHT|RENDERER_BACKBUFFER, resMan); + + uint8_t whiteData[]={255,255,255}; + texWhite.Create(1,1); + texWhite.Upload(0,GL_RGB,whiteData); + matWhite = new TexMaterial(&texWhite,true); + + progVolLight.CreateFromFile("volLight","volLight"); + progVolLight.u_mvpMtx = progVolLight.GetUniformLoc("u_mvpMtx"); + progVolLight.u_modelMtx = progVolLight.GetUniformLoc("u_modelMtx"); + progVolLight.u_cameraPos = progVolLight.GetUniformLoc("u_cameraPos"); + u_lightMtx = progVolLight.GetUniformLoc("u_lightMtx"); + u_invVPMtx = progVolLight.GetUniformLoc("u_invVPMtx"); + u_lightPosSize = progVolLight.GetUniformLoc("u_lightPosSize"); + progVolLight.UniformTex("u_lightDepth",1); + progVolLight.UniformTex("u_sceneDepth",2); + + progVolLightIn.CreateFromFile("volLight","volLightIn"); + progVolLightIn.u_mvpMtx = progVolLightIn.GetUniformLoc("u_mvpMtx"); + progVolLightIn.u_modelMtx = progVolLightIn.GetUniformLoc("u_modelMtx"); + progVolLightIn.u_cameraPos = progVolLightIn.GetUniformLoc("u_cameraPos"); + u_lightMtxIn = progVolLightIn.GetUniformLoc("u_lightMtx"); + u_invVPMtxIn = progVolLightIn.GetUniformLoc("u_invVPMtx"); + u_lightPosSizeIn = progVolLightIn.GetUniformLoc("u_lightPosSize"); + progVolLightIn.UniformTex("u_lightDepth",1); + progVolLightIn.UniformTex("u_sceneDepth",2); + + cfg.Load("config.txt"); + + Model *mdl = resMan->GetModel(cfg["modelName"].c_str()); + mdl->materials[0].mat = matWhite; + + scene = Scene(); + + StaticModel *obj1 = new StaticModel(); + obj1->mdl = mdl; + vec3 obj1Pos = cfg.GetVec3("modelPos"); + obj1->modelMtx = translate(mat4(1.0f),obj1Pos); + scene.AddObject(obj1); + + lightVol.Init(&scene,cfg.GetVec3("lightPos"),cfg.GetVec3("lightTarget"),atof(cfg["lightFOV"].c_str()), + atof(cfg["lightAspect"].c_str()),atof(cfg["lightNear"].c_str()),atof(cfg["lightFar"].c_str()),cfg.GetInt("lightRes")); + + volScale = atof(cfg["volScale"].c_str()); + volFBO.Create(); + volFBO.CreateTexture(64,64,GL_LINEAR); + FrameBufferObject::Unbind(); + + rend->SetScene(&scene); + + cam = Camera(); + cam.pos = cfg.GetVec3("cameraPos"); + cam.rot = cfg.GetVec3("cameraRot"); + cam.UpdateView(); + + rend->SetCamera(&cam); + + rend->debug = cfg["debug"]!="0"; + + joyL = Joystick(0,0.5,0.5,0.5); + //joyM = Joystick(0.5,0.5,0.5,0.5); + joyM = KeyJoystick(IN_KEY_W,IN_KEY_S,IN_KEY_D,IN_KEY_A); + frame = 0; + oldTime = GetTime(); +} + +void VolLightGame::Changed(int w, int h){ + if(!w||!h) + return; + + rend->Resize(w,h); + + cam.UpdateProj(80.0f,rend->aspect,0.02f,20.0f); + //cam.SetOrtho(aspect,bScroll.pos); + + volFBO.Resize(w*volScale,h*volScale); + frame = 0; +} + +void VolLightGame::Draw(){ + + double startTime = GetTime(); + float deltaTime = (startTime-oldTime); + oldTime = startTime; + + cam.pos += glm::inverse(glm::mat3(cam.viewMtx))*vec3(joyM.vel.x,0,-joyM.vel.y)*2.0f*deltaTime; + cam.rot += vec3(-joyL.vel.y*rend->aspect,-joyL.vel.x,0)*60.0f*deltaTime; + cam.UpdateView(); + + rend->Draw(); + + bool drawVolumetricLight = true; + bool isInsideVolume = lightVol.CheckPoint(cam.pos); + //drawVolumetricLight = !isInsideVolume; + + //Add light box to depth + if(drawVolumetricLight){ + rend->GetFBO()->Bind(); + rend->Set2DMode(false); + glColorMask(0,0,0,0); + //UseProg depth + lightVol.AddToDepth(rend); + glColorMask(1,1,1,1); + rend->GetFBO()->Unbind(); + } +#if 0 + //display scene depth + rend->Set2DMode(true); + rend->SetModelMtx(mat4(1)); + rend->GetFBO()->BindDepthTexture(); + rend->DrawRect(0.02,0.02,0.5,0.5); +#else + //prepare depth texture + if(drawVolumetricLight){ + if(frame%128==0) + lightVol.Prepare(rend); + + //Display light depth + /*lightFBO.BindDepthTexture(); + rend->SetModelMtx(mat4(1)); + rend->Set2DMode(); + rend->DrawRect(0.02,0.02,0.5,0.5);*/ + + volFBO.Bind(); + volFBO.Clear(1); + rend->Set2DMode(false); + if(!isInsideVolume){ + glCullFace(GL_FRONT); + rend->UseProg(&progVolLight); + lightVol.Draw(rend,u_lightMtx,u_invVPMtx,u_lightPosSize); + glCullFace(GL_BACK); + }else{ + rend->UseProg(&progVolLightIn); + lightVol.Draw(rend,u_lightMtxIn,u_invVPMtxIn,u_lightPosSizeIn); + } + + //clean up + glActiveTexture(GL_TEXTURE1); + texWhite.Bind(); + glActiveTexture(GL_TEXTURE2); + texWhite.Bind(); + glActiveTexture(GL_TEXTURE0); + + volFBO.Unbind(); + rend->ResetViewport(); + //draw effect additive + rend->Set2DMode(true); + glEnable(GL_BLEND); + glBlendFunc(1,1); + volFBO.BindTexture(); + rend->DrawRect(0.0,0.0,1.0,1.0); + glDisable(GL_BLEND); + } + texWhite.Bind(); +#endif + char temp[256]; + snprintf(temp,256,"volumetric light: %s",drawVolumetricLight?"On":"Off"); + rend->DrawText(temp,0.02,0.9,0.2*rend->aspect); + + frame++; +} + +Button::Button(float nx, float ny, float nw, float nh, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(0),active(true),pressed(false) +{ +} + +Button::Button(float nx, float ny, float nw, float nh, const char *t, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(t),active(true),pressed(false) +{ +} +void Button::Update(){} +bool Button::SetUniform(int loc){return false;} + +void VolLightGame::OnTouch(float tx, float ty, int ta, int tf){ + float x = tx/rend->width; +#ifdef ANDROID + float y = (ty-64)/rend->height; +#else + float y = ty/rend->height; +#endif + + if(ta==0){ + joyL.Hit(x,y,tf); + joyM.Hit(x,y*rend->aspect,tf); + }else if(ta==1){ + joyL.Release(tf); + joyM.Release(tf); + }else if(ta==2){ + joyL.Move(x,y,tf); + joyM.Move(x,y*rend->aspect,tf); + } +} + +void VolLightGame::OnKey(int key, int scancode, int action, int mods){ + joyM.OnKey(key,action); +} + diff --git a/demos/blur-test/Android.mk b/demos/blur-test/Android.mk new file mode 100644 index 0000000..02e7efe --- /dev/null +++ b/demos/blur-test/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../../nenuzhno-engine/ +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES = $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)file_system.cpp $(ENGINE_DIR)game/IGame.cpp \ + $(ENGINE_DIR)graphics/gl_utils.cpp $(ENGINE_DIR)graphics/gl_ext.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/fbo.cpp $(ENGINE_DIR)graphics/vbo.cpp \ + $(ENGINE_DIR)resource/vtf_loader.cpp $(ENGINE_DIR)resource/dds_loader.cpp \ + test.cpp $(ENGINE_DIR)renderer/BokehBlur.cpp +LOCAL_LDLIBS := -llog -lEGL -lGLESv2 +# -lm -lOpenSLES -lz + +include $(BUILD_SHARED_LIBRARY) + diff --git a/demos/blur-test/Makefile.mingw b/demos/blur-test/Makefile.mingw new file mode 100644 index 0000000..ebeaf83 --- /dev/null +++ b/demos/blur-test/Makefile.mingw @@ -0,0 +1,26 @@ +TARGET = ../../assets/blur-test.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include -I$(LIBS_DIR)/gl/glm/glm -I$(ENGINE_DIR)/src +CPPFLAGS = -Wall -g -ggdb -m32 -gdwarf-2 +LIBS = -lnenuzhno-engine -lglfw3 -lglew32 -lopengl32 -lgdi32 -L$(ENGINE_DIR)/bin -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw -L$(LIBS_DIR)/gl/glew-2.0.0/lib +LDFLAGS = -static-libgcc -static-libstdc++ -m32 -g -ggdb -gdwarf-2 +SRCS = blur.cpp +OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $(subst ..,_,$@) $(CPPFLAGS) $(INCLUDES) + +obj/_/graphics/gl_ext.o: ../graphics/gl_ext.cpp + $(CXX) -c $< -o $(subst ..,_,$@) $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(subst ..,_,$(OBJS)) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(OBJS) $(TARGET) + +rebuild: clean all diff --git a/demos/blur-test/blur.cpp b/demos/blur-test/blur.cpp new file mode 100644 index 0000000..1aa3735 --- /dev/null +++ b/demos/blur-test/blur.cpp @@ -0,0 +1,302 @@ + +#include +#include +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/glsl_prog.h" +#include "graphics/ArrayBuffer.h" +#include "graphics/fbo.h" +#include "graphics/gl_ext.h" + +#include "renderer/BokehBlur.h" +#include "game/IGame.h" + +class BlurGame : public IGame{ +public: + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "blur"; + } + + void DrawBlur(); +}; + +IGame *CreateGame(){ + return new BlurGame(); +} + +#define min(x,y) (xCreateFromFile("generic", "col_tex"); + progTex->u_mvpMtx = progTex->GetUniformLoc("u_mvpMtx"); + progTex->u_color = progTex->GetUniformLoc("u_color"); + progTex->Use(); + glUniform4f(progTex->u_color,1,1,1,1); + + progMip = new glslProg(); + progMip->CreateFromFile("generic", "tex_mip"); + progMip->u_mvpMtx = progMip->GetUniformLoc("u_mvpMtx"); + + // + glslProg tempProg("#version 100\nattribute vec4 a_p;void main(){gl_Position = a_p;}", + "#version 100\nprecision highp float; void main(){gl_FragColor = vec4(0.5);}"); + //"precision mediump float; attribute vec4 a_position;uniform mat4 u_mvpMtx; void main(){gl_Position = u_mvpMtx * a_position;gl_PointSize = 8.0;}", + //"precision highp float;void main(){gl_FragColor = vec4(0.5);}"); + + tempProg.Save("test"); + + char *tempData = 0; + int tempLen = 0; + int tempFmt = 0; + if(!tempProg.GetBinaryData(&tempFmt,&tempData,&tempLen)){ + Log("Can't get shader prog data\n"); + }else{ + progBin = new glslProg(); + progBin->CreateFromBinary(tempFmt,tempData,tempLen); + } + if(tempData) + delete[] tempData; + + glUseProgram(0); + + //*((int*)0)=42; + + float quadVerts[] ={ + 1,-1,1,0, + 1, 1,1,1, + -1, 1,0,1, + -1,-1,0,0 + }; + vboQuad = new VertexBufferObject(); + vboQuad->Create(); + vboQuad->Upload(4*4*4, quadVerts); + + float triVerts[] = + { + 0.8,-0.6,1,0, + 0.2, 0.9,1,1, + -0.9,-0.5,0.1,0.3 + }; + vboTri = new VertexBufferObject(); + vboTri->Create(); + vboTri->Upload(3*4*4, triVerts); + + GLubyte whiteTexData[]={ + 255 + }; + texWhite = new Texture(); + texWhite->Create(1,1); + texWhite->Upload(0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, whiteTexData); + + GLushort testTexData[] = + { + 0x0000,0x444F,0x888F,0xCCCF, 0x400F,0x800F,0xC00F,0xF00F, 0x040F,0x080F,0x0C0F,0x0F0F, 0x004F,0x008F,0x00CF,0x00FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x444F,0x844F,0xC44F,0xF44F, 0x444F,0x484F,0x4C4F,0x4F4F, 0x444F,0x448F,0x44CF,0x44FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x488F,0x888F,0xC88F,0xF88F, 0x848F,0x888F,0x8C8F,0x8F8F, 0x884F,0x888F,0x88CF,0x88FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x4ccF,0x8ccF,0xCccF,0xFccF, 0xc4cF,0xc8cF,0xcCcF,0xcFcF, 0xcc4F,0xcc8F,0xccCF,0xccFF, + 0x0000,0x444F,0x888F,0xCCCF, 0x440F,0x840F,0xC40F,0xF40F, 0x440F,0x480F,0x4C0F,0x4F0F, 0x404F,0x408F,0x40CF,0x40FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x480F,0x880F,0xC80F,0xF80F, 0x840F,0x880F,0x8C0F,0x8F0F, 0x804F,0x808F,0x80CF,0x80FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x4c0F,0x8c0F,0xCc0F,0xFc0F, 0xc40F,0xc80F,0xcC0F,0xcF0F, 0xc04F,0xc08F,0xc0CF,0xc0FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x4f0F,0x8f0F,0xCf0F,0xFf0F, 0xf40F,0xf80F,0xfC0F,0xfF0F, 0xf04F,0xf08F,0xf0CF,0xf0FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x404F,0x804F,0xC04F,0xF04F, 0x044F,0x084F,0x0C4F,0x0F4F, 0x044F,0x048F,0x04CF,0x04FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x408F,0x808F,0xC08F,0xF08F, 0x048F,0x088F,0x0C8F,0x0F8F, 0x084F,0x088F,0x08CF,0x08FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x40cF,0x80cF,0xC0cF,0xF0cF, 0x04cF,0x08cF,0x0CcF,0x0FcF, 0x0c4F,0x0c8F,0x0cCF,0x0cFF, + 0x0000,0x444F,0x888F,0xCCCF, 0x40fF,0x80fF,0xC0fF,0xF0fF, 0x04fF,0x08fF,0x0CfF,0x0FfF, 0x0f4F,0x0f8F,0x0fCF,0x0fFF, + 0x0000,0x444F,0x888F,0xCCCF, 0x400F,0x800F,0xC00F,0xF00F, 0x040F,0x080F,0x0C0F,0x0F0F, 0x004F,0x008F,0x00CF,0x00FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x400F,0x800F,0xC00F,0xF00F, 0x040F,0x080F,0x0C0F,0x0F0F, 0x004F,0x008F,0x00CF,0x00FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x400F,0x800F,0xC00F,0xF00F, 0x040F,0x080F,0x0C0F,0x0F0F, 0x004F,0x008F,0x00CF,0x00FF, + 0x0000,0x444F,0x888F,0xCCCF, 0x400F,0x800F,0xC00F,0xF00F, 0x040F,0x080F,0x0C0F,0x0F0F, 0x004F,0x008F,0x00CF,0x00FF + }; + + tex1 = new Texture(); + tex1->Create(16,16); + tex1->SetWrap(GL_CLAMP_TO_EDGE); + //tex1->SetFilter(GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST); + tex1->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + tex1->Upload(0, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, testTexData); + tex1->Upload(1, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, testTexData); + tex1->Upload(2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, testTexData); + tex1->Upload(3, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, testTexData+9); + tex1->Upload(4, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, testTexData+38); + + tex2 = new Texture(); + tex2->Create(64,64); + tex2->Upload(0,GL_RGBA4,GL_RGBA,GL_UNSIGNED_SHORT_4_4_4_4,0); +/* tex2->SetFilter(GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST); + for(int i=1;i<7;i++){ + tex2->Upload(i,GL_RGBA4,GL_RGBA,GL_UNSIGNED_SHORT_4_4_4_4,0); + } + tex2->Upload(2,GL_RGBA4,GL_RGBA,GL_UNSIGNED_SHORT_4_4_4_4,testTexData); +*/ + texFb1 = new Texture(); + texFb1->Create(64,64); + texFb1->SetFilter(GL_LINEAR, GL_LINEAR); + texFb1->Upload(0,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,0); + + fbo1 = new FrameBufferObject(); + fbo1->Create(); + fbo1->AttachTexture(tex2, 0); + + bokeh.Init(64,64,tex2); + + glBindTexture(GL_TEXTURE_2D,0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#ifndef ANDROID +//TODO: add defines to header + if(!(GLExtensions::extFlags&eGLES)){ + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glEnable(GL_POINT_SPRITE); + } +#endif + CheckGLError("Created", __FILE__, __LINE__); + Log("Init done\n"); +} + +void BlurGame::Changed(int w, int h) +{ + scrWidth=w; + scrHeight=h; + CheckGLError("Changed", __FILE__, __LINE__); +} +float a=0; +void BlurGame::Draw() +{ + a+=0.02f; + + progTex->Use(); + glUniformMatrix4fv(progTex->u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(2); + + DrawBlur(); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(2); + glBindTexture(GL_TEXTURE_2D,0); + + CheckGLError("Draw", __FILE__, __LINE__); +} + +void BlurGame::DrawBlur() +{ + bokehSize=(sin(a)+1.0f)*10.0f+0.1f; + //Log("boken size %f\n",bokehSize); + +#if 1 + fbo1->Bind(); + fbo1->AttachTexture(tex2, 0); + fbo1->SetupViewport(); + glClear(GL_COLOR_BUFFER_BIT); + + tex1->Bind(); + vboTri->Bind(); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + glDrawArrays(GL_TRIANGLES,0,3); + vboTri->Unbind(); +#if RENDER_TO_MIP + glBindFramebuffer(GL_FRAMEBUFFER, 0); + fbo1->Bind(); + fbo1->AttachTexture(tex2, 3); + glClear(GL_COLOR_BUFFER_BIT); +#endif +#if WHITE_LINE + texWhite->Bind(); + float lineVerts[]={ + -1,0.5,0,0, + 1,-0.5,0,0 + }; + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,lineVerts); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,lineVerts+2); + glDrawArrays(GL_LINES,0,2); +#endif + Texture *outTex=tex2; +#define POSTPROCESS 1 +#if POSTPROCESS + fbo1->AttachTexture(texFb1, 0); + fbo1->SetupViewport(); + glClear(GL_COLOR_BUFFER_BIT); + + glDisableVertexAttribArray(2); + bokeh.Render(bokehSize); + glEnableVertexAttribArray(2); + + outTex=texFb1; +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + //glViewport(0,0,scrWidth,scrHeight); + int d = min(scrWidth,scrHeight); + glViewport(0,0,d,d); + glClear(GL_COLOR_BUFFER_BIT); + + progTex->Use(); + + vboQuad->Bind(); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + //tex2->Bind(); + //glDrawArrays(GL_TRIANGLE_FAN,0,4); + outTex->Bind(); + //glEnable(GL_BLEND); + glBlendFunc(1,1); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + glDisable(GL_BLEND); + vboQuad->Unbind(); + +#else + int d = min(scrWidth,scrHeight); + glViewport(0,0,d,d); + progTex->Use(); + + bokeh.texCore->Bind(); + vboQuad->Bind(); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + vboQuad->Unbind(); +#endif + +#if SHOW_MIPS + progMip->Use(); + glUniformMatrix4fv(progMip->u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + + tex2->Bind(); + vboQuad->Bind(); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + vboQuad->Unbind(); +#endif +} + diff --git a/demos/cube/Android.mk b/demos/cube/Android.mk new file mode 100644 index 0000000..60b8842 --- /dev/null +++ b/demos/cube/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../../nenuzhno-engine/ +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)game/IGame.cpp $(ENGINE_DIR)renderer/camera.cpp $(ENGINE_DIR)/cull/frustum.cpp \ + $(ENGINE_DIR)graphics/gl_utils.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)resource/mesh_loader.cpp $(ENGINE_DIR)renderer/mesh.cpp $(ENGINE_DIR)graphics/vbo.cpp \ + cube.cpp +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/cube/Makefile.linux b/demos/cube/Makefile.linux new file mode 100644 index 0000000..af70304 --- /dev/null +++ b/demos/cube/Makefile.linux @@ -0,0 +1,17 @@ +TARGET = cube +ENGINE_DIR = ../../nenuzhno-engine + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR) -I../../../../Libs/gl/glm/glm -I../../../../Libs/gl/glew-2.1.0/include +CPPFLAGS = -Wall -ggdb +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR) -lGL -lglfw3 -lX11 -lXrandr -lXinerama -lXcursor -lpthread -ldl -lGLEW -L../../../../Libs/gl/glfw-3.2.1_src/lib -L../../../../Libs/gl/glew-2.1.0/lib +SRCS = cube.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/cube/Makefile.mingw b/demos/cube/Makefile.mingw new file mode 100644 index 0000000..543330d --- /dev/null +++ b/demos/cube/Makefile.mingw @@ -0,0 +1,18 @@ +TARGET = ../../assets/cube.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR)/src -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include +CPPFLAGS = -Wall -ggdb +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb +LIBS = -lnenuzhno-engine -lglfw3 -lglew32 -lgdi32 -lopengl32 -L$(ENGINE_DIR)/bin -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +SRCS = cube.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/cube/cube.cpp b/demos/cube/cube.cpp new file mode 100644 index 0000000..ed103d4 --- /dev/null +++ b/demos/cube/cube.cpp @@ -0,0 +1,114 @@ + +#include + +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/glsl_prog.h" +#include "graphics/ArrayBuffer.h" +#include "renderer/mesh.h" +#include "renderer/camera.h" +#include "game/IGame.h" +#include "engine.h" + +class CubeGame : public IGame{ +public: + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "base"; + } +}; + +IGame *CreateGame(){ + return new CubeGame(); +} + +GLfloat vertices[] = +{ + 0.5f, -0.5f, 0.5f, + 0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, 0.5f +}; + +int inds[] = +{ + 0,1, 1,2, 2,3, 3,0, + 4,5, 5,6, 6,7, 7,4, + 0,4, 1,5, 2,6, 3,7 +}; +int ni=24; + +int scrWidth = 0; +int scrHeight = 0; + +glslProg simpleProg; +Mesh *cube; +glm::mat4 modelMtx; +Camera camera; +glm::mat4 mvpMtx(1); + +void CubeGame::Created() +{ + Log("%s\n",glGetString(GL_VERSION)); + + simpleProg.CreateFromFile("simple", "simple"); + simpleProg.u_mvpMtx = simpleProg.GetUniformLoc("u_mvpMtx"); + + glUseProgram(0); + CheckGLError("Created shaders", __FILE__, __LINE__); + + //cube = LoadMeshFile("cube", true); + cube = new Mesh(vertices,8,(GLushort*)inds,ni,GL_LINES); + modelMtx = glm::scale(glm::mat4(1.0),glm::vec3(1.2,1.0,0.5)); + CheckGLError("Created meshes", __FILE__, __LINE__); + + //glm::scale(glm::translate(glm::mat4(1.0),glm::vec3(0,0.1,0)),glm::vec3(0.2)) + camera.pos = glm::vec3(0); + camera.rot = glm::vec3(30,30,0); + camera.UpdateView(); + + glBindTexture(GL_TEXTURE_2D, 0); + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); +// glEnable(GL_CULL_FACE); + CheckGLError("Created", __FILE__, __LINE__); +} + +void CubeGame::Changed(int w, int h) +{ + scrWidth = w; + scrHeight = h; + glViewport(0, 0, w, h); + float aspect = w/(float)h; + //camera.UpdateProj(75.0f, aspect, 0.1f, 2.0f); + camera.SetOrtho(aspect, 2.0f, 1.0f); +} + +void CubeGame::Draw() +{ + camera.UpdateView(); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + CheckGLError("Clear", __FILE__, __LINE__); + glEnableVertexAttribArray(0); + + simpleProg.Use(); + mvpMtx = camera.projMtx * camera.viewMtx * modelMtx; + glUniformMatrix4fv(simpleProg.u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + + cube->Bind(); + //cube->Draw(); + glDrawElements(GL_LINES, ni, GL_UNSIGNED_INT, inds); + cube->Unbind(); + if(CheckGLError("Draw cube", __FILE__, __LINE__)) + EngineError("stop"); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisableVertexAttribArray(0); + CheckGLError("Draw", __FILE__, __LINE__); +} diff --git a/demos/gravity/Android.mk b/demos/gravity/Android.mk new file mode 100644 index 0000000..ee3a4c6 --- /dev/null +++ b/demos/gravity/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../../nenuzhno-engine/ + +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)game/IGame.cpp \ + $(ENGINE_DIR)renderer/renderer.cpp $(ENGINE_DIR)renderer/LightingForward.cpp $(ENGINE_DIR)renderer/font.cpp $(ENGINE_DIR)renderer/mesh.cpp $(ENGINE_DIR)renderer/Model.cpp $(ENGINE_DIR)renderer/camera.cpp \ + $(ENGINE_DIR)graphics/vbo.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/fbo.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/gl_utils.cpp \ + $(ENGINE_DIR)scene/Scene.cpp $(ENGINE_DIR)cull/BoundingBox.cpp $(ENGINE_DIR)cull/Frustum.cpp \ + $(ENGINE_DIR)resource/ResourceManager.cpp $(ENGINE_DIR)resource/vtf_loader.cpp $(ENGINE_DIR)resource/dds_loader.cpp $(ENGINE_DIR)resource/nmf_loader.cpp $(ENGINE_DIR)resource/mesh_loader.cpp \ + main.cpp +# $(ENGINE_DIR)graphics/gl_ext.cpp $(ENGINE_DIR)graphics/vao.cpp + +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/gravity/Makefile.mingw b/demos/gravity/Makefile.mingw new file mode 100644 index 0000000..96ace54 --- /dev/null +++ b/demos/gravity/Makefile.mingw @@ -0,0 +1,18 @@ +TARGET = ../../assets/gravity.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR)/src -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include +CPPFLAGS = -Wall -ggdb -O2 +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb +LIBS = -lnenuzhno-engine -lglfw3 -lglew32 -lgdi32 -lopengl32 -L$(ENGINE_DIR)/bin -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +SRCS = main.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/gravity/main.cpp b/demos/gravity/main.cpp new file mode 100644 index 0000000..8578b1a --- /dev/null +++ b/demos/gravity/main.cpp @@ -0,0 +1,514 @@ + +#include "log.h" +#include "graphics/platform_gl.h" +#include "button.h" +#include "renderer/renderer.h" +#include "renderer/mesh.h" +#include "resource/ResourceManager.h" + +#include "game/IGame.h" + +#include +#include +#include +#include +#include +#include +using glm::vec2; +using glm::vec3; +using glm::linearRand; +using glm::clamp; +using glm::abs; +using glm::normalize; +using glm::translate; +using glm::scale; + +class vec3_queue{ +public: + vec3_queue():_data(){} + void push(vec3 v){ + _data.push_back(v); + for(int i=_data.size()-2;i>=0;i--){ + _data[i+1]=_data[i]; + } + _data[0] = v; + } + vec3 pop(){ + if(!_data.size()) + return vec3(0); + vec3 v = _data[_data.size()-1]; + _data.erase(_data.end()-1); + return v; + } + vec3& operator[] (int n){ + return _data[n]; + } + void *data(){ + return _data.data(); + } + int size(){ + return _data.size(); + } + void clear(){ + _data.clear(); + } +private: + std::vector _data; +}; + +class Entity +{ +public: + virtual ~Entity(){} + virtual void Update(float dt){} + virtual void UpdateTail(){} + virtual void Respawn(){} + + vec3 pos; + vec3 vel; + vec2 size; + float mass; + int hp; + + vec3_queue trail; +}; + +class Unit: public Entity +{ +public: + Unit(); + virtual ~Unit(){} + virtual void Update(float dt); + virtual void UpdateTail(); + virtual void Respawn(); + vec3 target; +}; + +Unit::Unit():Entity(),target(0){ + vel = vec3(0); + size = vec2(0.02f); + mass = 0.5f; + hp = 100; +}; + +Entity* Collide(Unit *u,vec3 orig, vec3 vel); +vec3 GetGravity(Unit *u); + +vec3 planetPos(0.0f,0.0f,0.0f); +const int asteroidsCount = 2048; +float planetRadius = 1; +float planetMass = 2000; +float G = 0.000066; +const float timeScale = 10; + +float aspect=1; + +float RadiusFromMass(float m) +{ + const float p = 5515.3; + float V = m/p; + return glm::pow((3*V)/(4* M_PI ),1.0/3.0); + //return glm::pow(m,0.5f)*0.02f; +} + +void Unit::Update(float dt) +{ + if(hp<=0) + return; + + float d=glm::distance(pos,planetPos); + if(d59) + { + planetMass += mass; + planetRadius = RadiusFromMass(planetMass); + Respawn(); + } + /* + if(pos.x<0||pos.x>aspect-size.x){ + vel.x*=-1; + //vel+=linearRand(vec3(-0.02,-0.06,0),vec3(0.02,0.06,0)); + pos.x=clamp(pos.x,0.0f,aspect-size.x); + } + if(pos.y<0||pos.y>1-size.y){ + vel.y*=-1; + //vel+=linearRand(vec3(-0.06,-0.02,0),vec3(0.06,0.02,0)); + pos.y=clamp(pos.y,0.0f,1-size.y); + } + */ + //vel+=vec3(0,0.5f*dt,0); + vec3 gravity = GetGravity(this); + vel += gravity/mass*dt; + + if(Entity *e = Collide(this,pos,vel*dt)) + { + /*if(abs(pos.x-e->pos.x)>abs(pos.y-e->pos.y)){ + vel.x*=-1; + e->vel.x*=-1; + }else{ + vel.y*=-1; + e->vel.y*=-1; + }*/ + + vel = (mass*vel + e->mass*e->vel)/(mass+e->mass); + pos = (mass*pos + e->mass*e->pos)/(mass+e->mass); + mass+=e->mass; + //e->Respawn(); + e->hp = 0; + } + + pos+=vel*dt; + if(trail.size()) + trail[0] = pos; + + size = vec2(RadiusFromMass(mass)); +} +void Unit::UpdateTail() +{ + if(hp<=0) + return; + + if(trail.size()>64){ + trail.pop(); + } + trail.push(pos); +} + +void Unit::Respawn() +{ + hp = 100; + + mass = linearRand(1.0f, 4.0f); + + size = vec2(RadiusFromMass(mass)); + const float rr = 12; + //pos = linearRand(vec3(-rr,-rr,0.0),vec3(rr,rr,0)); + pos = vec3(glm::diskRand(rr),0.0f); + pos += normalize(pos-planetPos)*planetRadius*1.2f; + //vel = linearRand(vec3(-0.6,-0.6,0),vec3(0.6,0.6,0)); + //vel = vec3(0); + + //vel = glm::cross(normalize(planetPos-pos),vec3(0,0,1.0f))*linearRand(-1.6f,1.6f); + vel = glm::cross(normalize(planetPos-pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-pos)))); + //vel *= (rand()&1 ? 1.0f : -1.0f); + vel *= linearRand(0.9f,1.1f); + + vel.z = 0; + trail.clear(); +} + +std::vector entities; + +Entity * Collide(Unit *u, vec3 orig, vec3 vel) +{ + //vec3 dst = orig+vel; + for(uint32_t i=0;ihp<=0) + continue; + /*if(e->pos.x>dst.x+u->size.x||e->pos.xsize.x) + continue; + if(e->pos.y>dst.y+u->size.y||e->pos.ysize.y) + continue;*/ + float d = glm::distance(e->pos,u->pos); + if(d > e->size.x+u->size.x) + continue; + return e; + } + + return 0; +} + + +vec3 GetGravity(Unit *u) +{ + vec3 g=vec3(0); + g += normalize(planetPos-u->pos)*G*((planetMass*u->mass)/glm::pow(glm::distance(u->pos,planetPos),2.0f)); + for(uint32_t i=0;ihp<=0) + continue; + vec3 d = e->pos-u->pos; + g += normalize(d)*G*((e->mass*u->mass)/glm::pow(glm::length(d),2.0f)); + } + return g; +} + +Mesh meshCircle; + +class GravityGame: public IGame{ +public: + GravityGame(){} + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "base"; + } + void OnTouch(float tx, float ty, int ta, int tf); + void DrawCircle(vec3 pos, float size); + IRenderer *rend; + ResourceManager *resMan; + Camera cam; + //vec2 pos; + + bool drawTrails; + Button bAdd; + Button bTrails; + Scroll bScroll; +}; + +IGame *CreateGame(){ + return new GravityGame(); +} + +Unit *CreateSatellite(float mass, float height, Unit *parent) +{ + Unit *un = new Unit(); + un->mass = mass; + un->size = vec2(RadiusFromMass(un->mass)); + if(parent) + un->pos = parent->pos + vec3(0, height, 0); + else + un->pos = vec3(0, planetRadius*1.2f+height, 0); + if(parent) + un->vel = parent->vel + glm::cross(normalize(parent->pos-un->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(parent->mass/(glm::length(parent->pos-un->pos)))); + else + un->vel = glm::cross(normalize(planetPos-un->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un->pos)))); + un->vel.z = 0; + un->trail.clear(); + + //un2->vel = glm::cross(normalize(planetPos-un2->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un2->pos)))); + //un2->vel += glm::cross(normalize(un->pos-un2->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(un->mass/(glm::length(un->pos-un2->pos)))); + + return un; +} + +void GravityGame::Created() +{ + srand(time(0)); + + Log("GravityGame Created()\n"); + resMan = new ResourceManager(); + resMan->Init(); + rend = CreateRenderer(); + rend->Init(RENDERER_GUI,resMan); + + int nv = 32*2; + float *v = new float[nv*2]; + int cv=0; + for(int i=0;i<32;i++){ + float a = i/32.0f*M_PI*2; + float b = (i+1)/32.0f*M_PI*2; + v[cv++] = sin(a); + v[cv++] = cos(a); + v[cv++] = sin(b); + v[cv++] = cos(b); + } + meshCircle = Mesh(v, nv, GL_LINES); + + planetRadius = RadiusFromMass(planetMass); + + for(int i=0;i<4;i++){ + Unit *un = new Unit(); + un->Respawn(); + entities.push_back(un); + } + + // + { + Unit *un1 = CreateSatellite(35, 10, NULL); + entities.push_back(un1); + + Unit *un2 = CreateSatellite(1, 0.8, un1); + entities.push_back(un2); + + Unit *un3 = new Unit(); + entities.push_back(un3); + un3->mass = 0.04f; + un3->size = vec2(RadiusFromMass(un3->mass)); + un3->pos = un2->pos+vec3(0,0.09,0); + un3->vel = glm::cross(normalize(planetPos-un3->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un3->pos)))); + un3->vel += glm::cross(normalize(un1->pos-un3->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(un1->mass/(glm::length(un1->pos-un3->pos)))); + un3->vel += glm::cross(normalize(un2->pos-un3->pos),vec3(0,0,1.0f)) * glm::sqrt(G*(un2->mass/(glm::length(un2->pos-un3->pos)))); + un3->vel.z = 0; + un3->trail.clear(); + } + + { + const float ir = 19; + const float cw = 2; + for(int i=0; imass = linearRand(0.04f, 2.0f); + un->size = vec2(RadiusFromMass(un->mass)); + un->pos = vec3(glm::circularRand(ir), 0.0f); + un->pos += normalize(un->pos - planetPos)*linearRand(0.0f, cw); + un->vel = glm::cross(normalize(planetPos-un->pos), vec3(0,0,1.0f)) * glm::sqrt(G*(planetMass/(glm::length(planetPos-un->pos)))); + //un->vel *= linearRand(0.9f,1.1f); + un->vel.z = 0; + un->trail.clear(); + entities.push_back(un); + } + } + // + + cam = Camera(); + + drawTrails = true; + + bAdd = Button(0.02,0.1,0.2,0.08,"Add"); + bScroll = Scroll(0.8,0.1,0.2,0.9); + bScroll.pos = 10; + bTrails = Button(0.25,0.1,0.3,0.08, "Draw trails"); + rend->AddButton(&bAdd); + rend->AddButton(&bTrails); + + +} + +void GravityGame::Changed(int w, int h) +{ + rend->Resize(w,h); + aspect = rend->aspect; + //planetPos.x = 0.5f*aspect; + cam.SetOrtho(aspect,bScroll.pos,1); +} + +void GravityGame::DrawCircle(vec3 pos, float size) +{ + mat4 mtx = translate(mat4(1.0f),pos); + mtx = scale(mtx,vec3(size)); + rend->SetModelMtx(mtx); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, meshCircle.verts); + glDrawArrays(meshCircle.mode,0,meshCircle.numVerts); +} + +void GravityGame::Draw() +{ + if(bAdd.pressed) + { + bAdd.pressed=0; + Unit *un = new Unit(); + un->Respawn(); + entities.push_back(un); + } + if(bTrails.pressed) + { + bTrails.pressed = false; + drawTrails = !drawTrails; + if(drawTrails) + { + for(size_t i = 0;itrail.clear(); + } + } + } + + cam.SetOrtho(aspect,bScroll.pos*2,1); + + float deltaTime = 0.02; + int subSteps = 10; + for(int i=0;iUpdate(deltaTime/subSteps * timeScale); + } + + for(size_t i = 0;ihp<=0){ + entities.erase(entities.begin()+i); + delete e; + i--; + } + } + } + static int t=0; + if(drawTrails){ + t++; + if(t%10==0){ + for(size_t i = 0;iUpdateTail(); + } + } + } + rend->Draw(); + //rend->DrawRect(0.1,0.1,0.1/rend->aspect,0.1); + //rend->DrawText("test",0.45*rend->aspect,0.1,0.6*rend->aspect); + + /*rend->DrawRect(pos.x,pos.y,0.1,0.1*rend->aspect); + char temp[256]; + snprintf(temp,256,"pos: (%.3f, %.3f)",pos.x,pos.y); + rend->DrawText(temp,0.1,0.2,0.2*rend->aspect); + */ + + char temp[256]; + snprintf(temp,256,"objects count: %d\nplanet mass %.3f",entities.size(), planetMass); + rend->DrawText(temp,0.02,0.9,0.2*rend->aspect); + + //mat4 mtx(1.0f); + rend->Set2DMode(); + rend->SetCamera(&cam); + glDisableVertexAttribArray(2); + for(size_t i = 0;iDrawRect(e->pos.x/aspect,e->pos.y,e->size.x/rend->aspect,e->size.y); + DrawCircle(e->pos,e->size.x); + } + + rend->SetModelMtx(mat4(1)); + if(drawTrails) + { + for(size_t i = 0;itrail.data()); + glDrawArrays(GL_LINE_STRIP,0,entities[i]->trail.size()); + } + } + /* + mtx = translate(mtx,planetPos); + rend->SetModelMtx(mtx); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, meshPlanet.verts); + glDrawArrays(meshPlanet.mode,0,meshPlanet.numVerts); + */ + DrawCircle(planetPos,planetRadius); +} + +Button::Button(float nx, float ny, float nw, float nh, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(0),active(true),pressed(false) +{ + /*if(adjust){ + if(aspect>1) + h*=aspect; + else + w/=aspect; + }*/ +} + +Button::Button(float nx, float ny, float nw, float nh, const char *t, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(t),active(true),pressed(false) +{ + //if(adjust) + // w/=aspect; +} +void Button::Update(){} +bool Button::SetUniform(int loc){return false;} + +void GravityGame::OnTouch(float tx, float ty, int ta, int tf){ + float x = tx/rend->width; +#ifdef ANDROID + float y = (ty-64)/rend->height; +#else + float y = ty/rend->height; +#endif + + if(ta==0){ + bAdd.Hit(x,y); + bTrails.Hit(x,y); + bScroll.Hit(x,y,tf); + }else if(ta==1){ + bScroll.Release(tf); + }else if(ta==2){ + bScroll.Move(x,y,tf); + } + //pos = vec2(x,y); +} diff --git a/demos/path_tracing/Makefile.mingw b/demos/path_tracing/Makefile.mingw new file mode 100644 index 0000000..5d99b5e --- /dev/null +++ b/demos/path_tracing/Makefile.mingw @@ -0,0 +1,18 @@ +TARGET = ../../assets/glsl_pt.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include -I$(LIBS_DIR)/gl/glm/glm -I$(ENGINE_DIR)/src +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR)/bin -lglfw3 -lglew32 -lopengl32 -lgdi32 -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +CPPFLAGS = -Wall -ggdb -m32 +LDFLAGS = -static-libgcc -static-libstdc++ -m32 +SRCS = glsl_pt.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) \ No newline at end of file diff --git a/demos/path_tracing/glsl_pt.cpp b/demos/path_tracing/glsl_pt.cpp new file mode 100644 index 0000000..feba46b --- /dev/null +++ b/demos/path_tracing/glsl_pt.cpp @@ -0,0 +1,439 @@ + +#include "log.h" +#include "engine.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/glsl_prog.h" +#include "graphics/ArrayBuffer.h" +#include "graphics/fbo.h" +#include "system/FileSystem.h" +#include "game/IGame.h" +#include "renderer/font.h" +#include "resource/ResourceManager.h" + +#include +#include + +#include +#include +#include +#include + +class rayGame: public IGame{ +public: + rayGame(); + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "pt"; + } + void OnTouch(float tx, float ty, int ta, int tf); +}; + +IGame *CreateGame(){ + return new rayGame(); +} + +char quadVert[]= +"#version 100\n" +"precision highp float;\n" +"attribute vec4 a_position;\n" +"varying vec2 v_uv;\n" +"void main(){\n" +" gl_Position = a_position;\n" +" v_uv = a_position.xy*0.5+0.5;\n" +"}\n"; + +char quadFrag[]= +"#version 100\n" +"precision highp float;\n" +"varying vec2 v_uv;\n" +"uniform sampler2D u_tex;\n" +"void main(){\n" +" gl_FragColor = texture2D(u_tex,v_uv);\n" +"}"; + +char quadTraceVert[]= +"#version 100\n" +"precision highp float;\n" +"attribute vec4 a_position;\n" +"varying vec2 v_uv;\n" +"void main(){\n" +" gl_Position = a_position;\n" +" v_uv = a_position.xy;\n" +"}\n"; + +char quadTraceFrag[]= +"#version 100\n" +"precision highp float;\n" +"varying vec2 v_uv;\n" +"uniform float u_textureWeight;\n" +"uniform sampler2D texture;\n" +"uniform sampler2D u_worldTex;\n" +"uniform float u_timeSinceStart;\n" +"uniform int u_numCubes;\n" +"const float glossiness = 0.8;\n" +"vec2 intersectCube(vec3 origin, vec3 ray, vec3 cubeMin, vec3 cubeMax){\n" +" vec3 tMin = (cubeMin - origin) / ray;\n" +" vec3 tMax = (cubeMax - origin) / ray;\n" +" vec3 t1 = min(tMin, tMax);\n" +" vec3 t2 = max(tMin, tMax);\n" +" float tNear = max(max(t1.x, t1.y), t1.z);\n" +" float tFar = min(min(t2.x, t2.y), t2.z);\n" +" return vec2(tNear, tFar);\n" +"}\n" +"vec3 normalForCube(vec3 hit, vec3 cubeMin, vec3 cubeMax){\n" +" if(hit.x < cubeMin.x + 0.0001) return vec3(-1.0, 0.0, 0.0);\n" +" else if(hit.x > cubeMax.x - 0.0001) return vec3(1.0, 0.0, 0.0);\n" +" else if(hit.y < cubeMin.y + 0.0001) return vec3(0.0, -1.0, 0.0);\n" +" else if(hit.y > cubeMax.y - 0.0001) return vec3(0.0, 1.0, 0.0);\n" +" else if(hit.z < cubeMin.z + 0.0001) return vec3(0.0, 0.0, -1.0);\n" +" else return vec3(0.0, 0.0, 1.0);\n" +"}\n" +"float random(vec3 scale, float seed){\n" +" return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);\n" +"}\n" +"vec3 cosineWeightedDirection(float seed, vec3 normal){\n" +" float u = random(vec3(12.9898, 78.233, 151.7182), seed);\n" +" float v = random(vec3(63.7264, 10.873, 623.6736), seed);\n" +" float r = sqrt(u);\n" +" float angle = 6.283185307179586 * v;\n" +// compute basis from normal +" vec3 sdir, tdir;\n" +" if (abs(normal.x)<.5){\n" +" sdir = cross(normal, vec3(1,0,0));\n" +" }else{\n" +" sdir = cross(normal, vec3(0,1,0));\n" +" }\n" +" tdir = cross(normal, sdir);\n" +" return r*cos(angle)*sdir + r*sin(angle)*tdir + sqrt(1.-u)*normal;\n" +"}\n" +"vec3 uniformlyRandomDirection(float seed){\n" +" float u = random(vec3(12.9898, 78.233, 151.7182), seed);\n" +" float v = random(vec3(63.7264, 10.873, 623.6736), seed);\n" +" float z = 1.0 - 2.0 * u;\n" +" float r = sqrt(1.0 - z * z);\n" +" float angle = 6.283185307179586 * v;\n" +" return vec3(r * cos(angle), r * sin(angle), z);\n" +"}\n" +"vec3 uniformlyRandomVector(float seed){\n" +" return uniformlyRandomDirection(seed) * sqrt(random(vec3(36.7539, 50.3658, 306.2759), seed));\n" +"}\n" +"float shadow(vec3 origin, vec3 ray) {\n" +" for(int c = 0; c 0.0 && tCube1.x < 1.0 && tCube1.x < tCube1.y)\n" +" return 0.0;\n" +" }\n" +" return 1.0;\n" +"}\n" +//"vec3 cube1min = vec3(-0.2,-0.2,-0.2);\n" +//"vec3 cube1max = vec3(0.2,0.2,0.2);\n" +//texture2D(u_worldTex,vec2(0,0)).xyz, texture2D(u_worldTex,vec2(0,0)).xyz +"vec3 calculateColor(vec3 origin, vec3 ray, vec3 light,float seed){\n" +" vec3 colorMask = vec3(1.0);\n" +" vec3 result = vec3(0.0);\n" +" for(int bounce = 0; bounce < 4; bounce++){\n" +" float t = 9999.9;\n" +" vec3 normal;\n" +" vec2 tRoom = intersectCube(origin, ray, vec3(-1.5,-1.0,-1.0), vec3(1.0));\n" +" if(tRoom.x < tRoom.y) t = tRoom.y;\n" +" vec3 hit = origin + ray * t;\n" +" for(int c = 0; c 0.0 && tCube1.x < tCube1.y && tCube1.x < t){\n" +" t = tCube1.x;\n" +" hit = origin + ray * t;\n" +" normal = normalForCube(hit, cubemin,cubemax);\n" +" }\n" +" }\n" +" vec3 surfaceColor = vec3(0.75);\n" +" if(t == tRoom.y){\n" +" normal = -normalForCube(hit, vec3(-1.5,-1.0,-1.0), vec3(1.0));\n" +" if(hit.x < -1.4999) surfaceColor = vec3(1.0, 0.3, 0.1);\n" // red +" else if(hit.x > 0.9999) surfaceColor = vec3(0.3, 1.0, 0.1);\n" // green +" ray = cosineWeightedDirection(seed + float(bounce), normal);\n" +//" ray = reflect(ray, normal);\n" +//" ray = normalize(reflect(ray, normal)) + uniformlyRandomVector(seed + float(bounce)) * glossiness;\n" +" }else if(t == 9999.9){\n" +" break;\n" +" }else{\n" +//" if(t == tCube1.x && tCube1.x < tCube1.y)" +//" normal = normalForCube(hit, texture2D(u_worldTex,vec2(0,0)).xyz,texture2D(u_worldTex,vec2(1,0)).xyz);\n" +//" ray = reflect(ray, normal);\n" +" ray = normalize(reflect(ray, normal)) + uniformlyRandomVector(seed + float(bounce)) * glossiness;\n" +" surfaceColor = vec3(0.5, 0.5, 0.9);\n" +" }\n" +" vec3 toLight = light - hit;\n" +" float diffuse = max(0.0, dot(normalize(toLight), normal));\n" +" float shadowIntensity = shadow(hit + normal * 0.0001, toLight);\n" +" colorMask *= surfaceColor;\n" +" result += colorMask*diffuse*0.5*shadowIntensity;\n" +" origin = hit;\n" +" }\n" +" return result;\n" +"}\n" +"void main(){\n" +" vec3 dir = normalize(vec3(v_uv,-1.0));\n" +" vec3 newLight = vec3(-1.4,0.1,-0.1) + uniformlyRandomVector(u_timeSinceStart - 53.0) * 0.1;\n" +" vec3 sample1 = calculateColor(vec3(-0.2,0.15,1.5), dir, newLight,u_timeSinceStart);\n" +" newLight = vec3(-1.4,0.1,-0.1) + uniformlyRandomVector(u_timeSinceStart - 27.4) * 0.1;\n" +" sample1 = 0.5*(sample1+calculateColor(vec3(-0.2,0.15,1.5), dir, newLight,u_timeSinceStart+65.3));\n" +" vec3 textureCol = texture2D(texture, v_uv*0.5+0.5).rgb;\n" +" gl_FragColor = vec4(mix(sample1, textureCol, u_textureWeight), 1.0);\n" +"}\n"; + +float verts[]={ + -1,-1,0, + -1,1,0, + 1,1,0, + 1,-1,0, + 0.5f,-0.25f,0 +}; + +float world[]={ + -0.2,-0.2,-0.2, 0.2,0.2,0.2, + -1.5,-1.0,-1.0, -1.2,-0.2,1.0, + -1.5,-0.2,-1.0, -1.2,0.2,-0.2, + -1.5,-0.2,0.2, -1.2,0.2,1.0, + -1.5,0.2,-1.0, -1.2,1.0,1.0 +}; + +glslProg progTex; +glslProg progQuad; +glslProg progQuadTrace; +GLuint u_textureWeight; +GLuint u_timeSinceStart; +GLuint u_worldTex; +GLuint u_numCubes; + +VertexBufferObject vbo; +FrameBufferObject fbo; +Texture textures[2]; +Texture texWhite; + +int texSize = 512; + +Texture worldTex; +int scrW; +int scrH; +bool needRedraw = true; +int samples = 0; +int maxSamples = 32; + +ResourceManager g_resMan; +Font font; + +double oldTime; +float deltaTime; +int fps; +float curTime; +int curFrames; + +void flip_tex(Texture *val) +{ + int t = val[0].id; + val[0].id = val[1].id; + val[1].id = t; +} + +rayGame::rayGame() +{ + +} + +void rayGame::Created() +{ + g_resMan.Init(); + font.LoadBMFont("sansation",&g_resMan); + + Log("%s\n",glGetString(GL_VERSION)); + + /*g_fs.WriteAll("shaders/quadTrace.vs",quadTraceVert); + g_fs.WriteAll("shaders/quadTrace.fs",quadTraceFrag); + g_fs.WriteAll("shaders/quad.vs",quadVert); + g_fs.WriteAll("shaders/quad.fs",quadFrag); + */ + glClearColor(0.0f,0.0f,0.0f,1.0f); + /* + progQuad = glslProg(quadVert, quadFrag); + CheckGLError("Create progQuad", __FILE__, __LINE__); + progQuadTrace = glslProg(quadTraceVert, quadTraceFrag); + CheckGLError("Create progQuadTrace", __FILE__, __LINE__);*/ + progTex.CreateFromFile("tex","tex"); + progTex.u_mvpMtx = progTex.GetUniformLoc("u_mvpMtx"); + progQuad.CreateFromFile("quad","quad"); + progQuadTrace.CreateFromFile("quadTrace","quadTrace"); + u_textureWeight = progQuadTrace.GetUniformLoc("u_textureWeight"); + u_timeSinceStart = progQuadTrace.GetUniformLoc("u_timeSinceStart"); + u_worldTex = progQuadTrace.GetUniformLoc("u_worldTex"); + u_numCubes = progQuadTrace.GetUniformLoc("u_numCubes"); + CheckGLError("CreatePrograms", __FILE__, __LINE__); + + progQuadTrace.Use(); + glUniform1i(u_worldTex,1); + glUniform1i(u_numCubes,5); + + vbo.Create(); + vbo.Upload(5*3*4,verts); + + glBindBuffer(GL_ARRAY_BUFFER,0); + + for(int i=0;i<2;i++){ + textures[i].Create(texSize,texSize); + textures[i].Bind(); + + //glPixelStorei(GL_UNPACK_ALIGNMENT,4); + //glPixelStorei(GL_PACK_ALIGNMENT,1); + + textures[i].SetFilter(GL_NEAREST,GL_LINEAR); + + //glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,texSize,texSize,0,GL_RGB,GL_UNSIGNED_BYTE,NULL); + textures[i].Upload(0, GL_RGB, NULL); + } + + worldTex.Create(2,5); + //glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,2,5,0,GL_RGB,GL_FLOAT,world); + worldTex.Upload(0,GL_RGB32F,GL_RGB,GL_FLOAT,(GLubyte*)world); + + GLubyte whitePix[]={255,255,255}; + texWhite.Create(1,1); + texWhite.Upload(0,GL_RGB,whitePix); + + glBindTexture(GL_TEXTURE_2D, 0); + + + fbo.Create(); + //fbo.CreateTexture(texSize, texSize, GL_LINEAR); +// glBindFramebuffer(GL_FRAMEBUFFER,fbo); +// glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,textures[0],0); + glBindFramebuffer(GL_FRAMEBUFFER,0); + + CheckGLError("Created", __FILE__, __LINE__); + srand(time(0)); + + oldTime = GetTime(); + curTime=0; + curFrames=0; +} + +void ResizeTextures(int r){ + texSize = r; + textures[0].Bind(); + textures[0].Upload(0, r, r, NULL); + textures[1].Bind(); + textures[1].Upload(0, r, r, NULL); +} + +void rayGame::Changed(int w, int h){ +// scrW = w; +// scrH = h; + scrW = scrH = fmin(w, h); + glViewport(0, 0, scrW, scrH); + samples = 0; + needRedraw = true; + ResizeTextures(scrH); +} + +void Update(){ +} + +void rayGame::Draw(){ + + double startTime = GetTime(); + float deltaTime = (startTime-oldTime); + oldTime = startTime; + curTime+=deltaTime; + if(curTime>=1){ + curTime-=1; + fps = curFrames; + curFrames=0; + } + curFrames++; + vbo.Bind(); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,12,0); + //CheckGLError("Draw1", __FILE__, __LINE__); + + while(needRedraw){ + //if(needRedraw){ + progQuadTrace.Use(); + + glUniform1f(u_timeSinceStart,(float)rand()/9276714.73f); + glUniform1f(u_textureWeight,(float)samples/(samples+1)); + //CheckGLError("Draw2", __FILE__, __LINE__); + + glActiveTexture(GL_TEXTURE1); + worldTex.Bind(); + glActiveTexture(GL_TEXTURE0); + textures[0].Bind(); + //CheckGLError("Draw3", __FILE__, __LINE__); + + fbo.Bind(); + //CheckGLError("Draw4", __FILE__, __LINE__); + glViewport(0,0,texSize,texSize); + //glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,textures[1],0); + fbo.AttachTexture(&textures[1]); + + //glClear(GL_COLOR_BUFFER_BIT); + + glDrawArrays(GL_TRIANGLE_FAN,0,4); + glBindFramebuffer(GL_FRAMEBUFFER,0); + + glBindTexture(GL_TEXTURE_2D,0); + + flip_tex(textures); + samples++; + + if(samples>=maxSamples) + needRedraw=false; + } + //Log("Draw %d\n", __LINE__); +// glBindFramebuffer(GL_FRAMEBUFFER,0); + glViewport(0,0,scrW,scrH); + + progQuad.Use(); + textures[0].Bind(); + // + //worldTex.Bind(); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + + glBindTexture(GL_TEXTURE_2D,0); + + + progTex.Use(); + glm::mat4 mtx(1.0f); + mtx = glm::translate(mtx,glm::vec3(-1.0,-1.0,0.0)); + float aspect = 1;//TODO + mtx = glm::scale(mtx,glm::vec3(2.0/aspect,2.0f,1.0f)); + glUniformMatrix4fv(progTex.u_mvpMtx,1,false,glm::value_ptr(mtx)); + + glActiveTexture(GL_TEXTURE0); + + glBindBuffer(GL_ARRAY_BUFFER,0); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(2); + + char t[256]; + snprintf(t,256,"fps: %d\n""res: %dx%d\n""samples: %d",fps,texSize,texSize,samples); + font.Print(t,0.02,0.12,0.5); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(2); + + needRedraw = true; + samples=0; + + CheckGLError("Draw", __FILE__, __LINE__); +} + +void rayGame::OnTouch(float tx, float ty, int ta, int tf){ + + samples = 0; + needRedraw = true; +} diff --git a/demos/skinning/Android.mk b/demos/skinning/Android.mk new file mode 100644 index 0000000..ec24021 --- /dev/null +++ b/demos/skinning/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR =../../nenuzhno-engine/ +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall -std=c++11 +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)system/config.cpp $(ENGINE_DIR)game/IGame.cpp $(ENGINE_DIR)/cull/frustum.cpp $(ENGINE_DIR)cull/BoundingBox.cpp\ + $(ENGINE_DIR)graphics/gl_utils.cpp $(ENGINE_DIR)graphics/gl_ext.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/vbo.cpp $(ENGINE_DIR)graphics/vao.cpp $(ENGINE_DIR)graphics/fbo.cpp \ + $(ENGINE_DIR)renderer/renderer.cpp $(ENGINE_DIR)renderer/LightingForward.cpp $(ENGINE_DIR)renderer/mesh.cpp $(ENGINE_DIR)renderer/Model.cpp $(ENGINE_DIR)renderer/camera.cpp $(ENGINE_DIR)renderer/font.cpp \ + $(ENGINE_DIR)resource/ResourceManager.cpp $(ENGINE_DIR)resource/vtf_loader.cpp $(ENGINE_DIR)resource/dds_loader.cpp $(ENGINE_DIR)resource/nmf_loader.cpp $(ENGINE_DIR)resource/mesh_loader.cpp \ + $(ENGINE_DIR)scene/Scene.cpp \ + main.cpp mdl_loader.cpp +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/skinning/CMakeLists.txt b/demos/skinning/CMakeLists.txt new file mode 100644 index 0000000..c30c77d --- /dev/null +++ b/demos/skinning/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8) + +project(skinning) + +#engine +include_directories(../../nenuzhno-engine) +add_library(nenuzhno-engine STATIC IMPORTED) +set_property(TARGET nenuzhno-engine PROPERTY IMPORTED_LOCATION ../../nenuzhno-engine/libnenuzhno-engine.a) +#add_subdirectory(../../nenuzhno-engine ../../build-nenuzhno-engine-Desktop32-Default) + +#glfw +add_library(glfw STATIC IMPORTED) +set_property(TARGET glfw PROPERTY IMPORTED_LOCATION ../../../../Libs/gl/glfw-3.1.2/lib-mingw/libglfw3.a) + +#glew +include_directories(../../../../Libs/gl/glew-2.0.0/include) +add_library(glew STATIC IMPORTED) +set_property(TARGET glew PROPERTY IMPORTED_LOCATION ../../../../Libs/gl/glew-2.0.0/lib/libglew32.a) + +#glm +include_directories(../../../../Libs/gl/glm-0.9.8.4/glm) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + +add_executable(${PROJECT_NAME} "main.cpp") + +target_link_libraries(${PROJECT_NAME} nenuzhno-engine glfw glew OpenGL32) diff --git a/demos/skinning/Makefile.linux b/demos/skinning/Makefile.linux new file mode 100644 index 0000000..bd0695f --- /dev/null +++ b/demos/skinning/Makefile.linux @@ -0,0 +1,17 @@ +TARGET = skinning +ENGINE_DIR = ../../nenuzhno-engine + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR) -I../../../../Libs/gl/glm/glm -I../../../../Libs/gl/glew-2.1.0/include -I../../../../Libs/gl/glfw-3.2.1_src/include +CPPFLAGS = -Wall -ggdb +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR) -lGL -lglfw3 -lX11 -lXrandr -lXinerama -lXcursor -lpthread -ldl -lGLEW -L../../../../Libs/gl/glfw-3.2.1_src/lib -L../../../../Libs/gl/glew-2.1.0/lib +SRCS = main.cpp mdl_loader.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/skinning/Makefile.mingw b/demos/skinning/Makefile.mingw new file mode 100644 index 0000000..ddb56df --- /dev/null +++ b/demos/skinning/Makefile.mingw @@ -0,0 +1,18 @@ +TARGET = ../../assets/skinning.exe +ENGINE_DIR = ../.. +LIBS_DIR = ../../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR)/src -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include +CPPFLAGS = -Wall -ggdb -m32 -std=c++11 +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb -m32 +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR)/bin -lopengl32 -lglfw3 -lglew32 -lgdi32 -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib +SRCS = main.cpp mdl_loader.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) diff --git a/demos/skinning/README.md b/demos/skinning/README.md new file mode 100644 index 0000000..d248c26 --- /dev/null +++ b/demos/skinning/README.md @@ -0,0 +1 @@ +=MDL models viewer with skinning= \ No newline at end of file diff --git a/demos/skinning/main.cpp b/demos/skinning/main.cpp new file mode 100644 index 0000000..4e05479 --- /dev/null +++ b/demos/skinning/main.cpp @@ -0,0 +1,458 @@ + +#include "log.h" +#include "engine.h" +#include "game/IGame.h" +#include "system/config.h" +#include "graphics/gl_utils.h" +#include "graphics/gl_ext.h" +#include "graphics/glsl_prog.h" +#include "graphics/texture.h" +#include "graphics/vao.h" +#include "renderer/Model.h" +#include "renderer/mesh.h" +#include "renderer/camera.h" +#include "renderer/font.h" +#include "button.h" + +#include "mdl_loader.h" + +#include +#include +#include +#include +using glm::vec3; +using glm::vec4; +using glm::mat3; +using glm::mat4; +using glm::translate; +using glm::scale; +using glm::radians; + +void VAOFromModel(Model *mdl, VertexArrayObject *out); +void VAOFromMDL(Model *mdl, VertexArrayObject *out,int a_weight,int a_bones); +void GenRandomFrame(mat4 *out, mat4 *base, mat4 *inv, Model *mdl); +void InterpFrames(float a,mat4 *f1, mat4 *f2, mat4 *out,int num); +void MeshFromModelSkeleton(Mesh *mesh, Model *mdl, mat4 *bonesMtx); +void UpdateSkeletonMesh(Mesh *mesh, Model *mdl, mat4 *bonesMtx); +void DrawModel(Model *mdl,VertexArrayObject *vao); +void DrawMesh(Mesh *mesh); + +class skinGame: public IGame{ +public: + skinGame(){} + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "skinning"; + } + void OnTouch(float tx, float ty, int ta, int tf); + + int width; + int height; + float aspect; + + ResourceManager resMan; + Font *font; + glslProg progTex; + glslProg progSkin; + int a_weight; + int a_bones; + int u_bonesMtx; + + Texture texWhite; + Model *mdlTest; + VertexArrayObject vaoTest; + Camera camera; + mat4 mvpMtx; + mat4 modelMtx; + mat4 *bonesBaseMtx; + mat4 *bonesInvMtx; + mat4 *bonesMtx; + mat4 *frame1Mtx; + mat4 *frame2Mtx; + mat4 *skelMeshMtx; + Mesh meshSkeleton; + float curTime; + int animIdx; + + Joystick joyL; + Joystick joyM; + Button bNext; + Button bPrev; + + double oldTime; +}; + +IGame *CreateGame(){ + return new skinGame(); +} + +void skinGame::Created() +{ + srand(time(NULL)); + Log("skinning test Created()\n"); + + GLExtensions::Init(); + + resMan.Init(); + resMan.AddModelLoader(new MDLLoader(&resMan)); + + progTex.CreateFromFile("generic","col_tex"); + progTex.u_mvpMtx = progTex.GetUniformLoc("u_mvpMtx"); + progTex.u_color = progTex.GetUniformLoc("u_color"); + + progSkin.CreateFromFile("skinning","skinning"); + progSkin.u_mvpMtx = progSkin.GetUniformLoc("u_mvpMtx"); + progSkin.u_modelMtx = progSkin.GetUniformLoc("u_modelMtx"); + progSkin.u_color = progSkin.GetUniformLoc("u_color"); + u_bonesMtx = progSkin.GetUniformLoc("u_bonesMtx"); + a_weight = progSkin.GetAttribLoc("a_weight"); + a_bones = progSkin.GetAttribLoc("a_bones"); + + CheckGLError("Created shaders", __FILE__, __LINE__); + + font = new Font(); + font->LoadBMFont("sansation",&resMan); + + uint8_t texWhiteData[1] = {255}; + texWhite.Create(1, 1); + texWhite.Upload(0, GL_LUMINANCE, texWhiteData); + glBindTexture(GL_TEXTURE_2D, 0); + CheckGLError("Created textures", __FILE__, __LINE__); + + ConfigFile cfg; + cfg.Load("config.txt"); + animIdx = cfg.GetInt("anim"); + modelMtx = scale(mat4(1),cfg.GetVec3("scale")); + modelMtx = rotate(modelMtx,radians(-90.0f),vec3(1,0,0)); + + mdlTest = resMan.GetModel(cfg["model"].c_str()); + if(cfg["model"]=="models/player.mdl"){ + mdlTest->materials.Resize(6); + mdlTest->materials[0].mat=new TexMaterial(resMan.GetTexture("chell/chell_head_diffuse.vtf")); + mdlTest->materials[1].mat=new TexMaterial(resMan.GetTexture("chell/gambler_eyes.vtf")); + mdlTest->materials[2].mat=new TexMaterial(resMan.GetTexture("chell/gambler_eyes.vtf")); + mdlTest->materials[3].mat=new TexMaterial(resMan.GetTexture("chell/chell_torso_diffuse.vtf")); + mdlTest->materials[4].mat=new TexMaterial(resMan.GetTexture("chell/chell_legs_diffuse.vtf")); + mdlTest->materials[5].mat=new TexMaterial(resMan.GetTexture("chell/chell_hair.vtf")); + + modelMtx = rotate(modelMtx,radians(90.0f),vec3(1,0,0)); + } + if(cfg["model"]=="models/turret.mdl"){ + mdlTest->materials.Resize(4); + mdlTest->materials[0].mat=new TexMaterial(resMan.GetTexture("turret/turret_frame02.vtf")); + mdlTest->materials[1].mat=new TexMaterial(resMan.GetTexture("turret/turret_frame01.vtf")); + mdlTest->materials[2].mat=new TexMaterial(resMan.GetTexture("turret/turret_casing.vtf")); + mdlTest->materials[3].mat=new TexMaterial(resMan.GetTexture("turret/turret_eye.vtf")); + } + if(cfg["model"]=="models/headcrabclassic.mdl"){ + mdlTest->materials.Resize(1); + mdlTest->materials[0].mat=new TexMaterial(resMan.GetTexture("headcrabsheet.vtf")); + } + if(cfg["model"]=="models/male_07.mdl"){ + mdlTest->materials.Resize(8); + mdlTest->materials[2].mat=new TexMaterial(resMan.GetTexture("group03/mike_facemap.vtf")); + mdlTest->materials[3].mat=new TexMaterial(resMan.GetTexture("group03/citizen_sheet.vtf")); + } + VAOFromMDL(mdlTest,&vaoTest,a_weight,a_bones); + //mdlTest = resMan.GetModel("cube.nmf"); + //VAOFromModel(mdlTest,&vaoTest); + + bonesBaseMtx = new mat4[mdlTest->skeleton.size]; + bonesInvMtx = new mat4[mdlTest->skeleton.size]; + bonesMtx = new mat4[mdlTest->skeleton.size]; + frame1Mtx = new mat4[mdlTest->skeleton.size]; + frame2Mtx = new mat4[mdlTest->skeleton.size]; + skelMeshMtx = new mat4[mdlTest->skeleton.size]; + for(int i=0;iskeleton.size;i++){ + bonesBaseMtx[i] = glm::mat4_cast(mdlTest->skeleton[i].rot); + bonesBaseMtx[i][3] = vec4(mdlTest->skeleton[i].pos,1.0f); + if(mdlTest->skeleton[i].parent!=-1) + bonesBaseMtx[i] = bonesBaseMtx[mdlTest->skeleton[i].parent] * bonesBaseMtx[i]; + bonesInvMtx[i] = glm::inverse(bonesBaseMtx[i]); + bonesMtx[i] = mat4(1); + } + MeshFromModelSkeleton(&meshSkeleton,mdlTest,bonesBaseMtx); + curTime=0; + + glClearColor(0.3,0.3,0.3,1.0); + + camera.pos = vec3(0,1.5,1.6); + camera.rot = vec3(10,0,0); + camera.UpdateView(); + + joyL = Joystick(0,0.5,0.5,0.5); + joyM = Joystick(0.5,0.5,0.5,0.5); + bNext = Button(0,0,0.1,0.1); + bPrev = Button(0.2,0,0.1,0.1); + + oldTime = GetTime(); +} + +void GenRandomFrame(mat4 *out, mat4 *base, mat4 *inv, Model *mdl){ + for(int i=0;iskeleton.size;i++){ + if(i!=0){ + out[i] = base[i]*glm::mat4_cast(glm::quat(glm::linearRand(vec3(-0.2f),vec3(0.2f))))*inv[i]; + if(mdl->skeleton[i].parent!=-1){ + out[i] = out[mdl->skeleton[i].parent] * out[i]; + } + } + } +} +/* +void InterpFramesMtx(float a,mat4 *f1, mat4 *f2, mat4 *out,int num){ + for(int i=0;i=anim.frames.size) + b = anim.frames.size-1; + float s = frame-a; + AnimFrame_t &fr1 = anim.frames[a]; + AnimFrame_t &fr2 = anim.frames[b]; + for(int i=0;ianimations.size) + animIdx=(animIdx+1)%mdlTest->animations.size; + curTime=0; + /*GenRandomFrame(bonesMtx,bonesBaseMtx,bonesInvMtx,mdlTest); + for(int i=0;iskeleton.size;i++){ + skelMeshMtx[i] = bonesMtx[i]*bonesBaseMtx[i]; + } + UpdateSkeletonMesh(&meshSkeleton,mdlTest,skelMeshMtx);*/ + } + if(bPrev.pressed){ + bPrev.pressed=false; + if(mdlTest->animations.size) + animIdx=(animIdx-1+mdlTest->animations.size)%mdlTest->animations.size; + curTime=0; + } + + curTime+=deltaTime*0.5; +/* + if(curTime>=1){ + memcpy(frame1Mtx,frame2Mtx,sizeof(mat4)*mdlTest->skeleton.size); + GenRandomFrame(frame2Mtx,bonesBaseMtx,bonesInvMtx,mdlTest); + curTime = 0; + } + InterpFramesMtx(curTime,frame1Mtx,frame2Mtx,bonesMtx,mdlTest->skeleton.size); + for(int i=0;iskeleton.size;i++){ + skelMeshMtx[i] = bonesMtx[i]*bonesBaseMtx[i]; + } + UpdateSkeletonMesh(&meshSkeleton,mdlTest,skelMeshMtx); +*/ + float curFrame = 0; + if(mdlTest->animations.size){ + curFrame = curTime*mdlTest->animations[animIdx].fps; + while(curFrame>=mdlTest->animations[animIdx].frames.size) + curFrame -= mdlTest->animations[animIdx].frames.size; + SetupFrame(skelMeshMtx,mdlTest->animations[animIdx],curFrame); + UpdateSkeletonMesh(&meshSkeleton,mdlTest,skelMeshMtx); + for(int i=0;iskeleton.size;i++){ + bonesMtx[i] = skelMeshMtx[i]*bonesInvMtx[i]; + } + } + + camera.pos += glm::inverse(mat3(camera.viewMtx))*vec3(joyM.vel.x,0,-joyM.vel.y*aspect)*5.0f*deltaTime; + camera.rot += vec3(-joyL.vel.y*aspect,-joyL.vel.x,0)*90.0f*deltaTime; + camera.UpdateView(); + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + mvpMtx = camera.projMtx*camera.viewMtx*modelMtx; + + glEnable(GL_DEPTH_TEST); + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + texWhite.Bind(); + glslProg *progCur = &progSkin; + //progCur = &progTex; + progCur->Use(); + progCur->UniformMat4(progCur->u_mvpMtx,mvpMtx); + if(progCur->u_modelMtx>=0) + progCur->UniformMat4(progCur->u_modelMtx,modelMtx); + if(progCur->u_color>=0) + progCur->UniformVec4(progCur->u_color,vec4(1)); + if(u_bonesMtx>=0){ + glUniformMatrix4fv(u_bonesMtx,mdlTest->skeleton.size,false,glm::value_ptr(bonesMtx[0])); + } + + DrawModel(mdlTest,&vaoTest); + + mvpMtx = camera.projMtx*camera.viewMtx*modelMtx; + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnableVertexAttribArray(0); + progCur = &progTex; + progCur->Use(); + progCur->UniformMat4(progCur->u_mvpMtx,mvpMtx); + if(progCur->u_color>=0) + progCur->UniformVec4(progCur->u_color,vec4(1)); + texWhite.Bind(); + DrawMesh(&meshSkeleton); + + glDisableVertexAttribArray(1); + + glEnableVertexAttribArray(2); + + mat4 mtx2D = mat4(1); + mtx2D = glm::translate(mtx2D,glm::vec3(-1,-1,0)); + mtx2D = glm::scale(mtx2D,glm::vec3(2.0f,2.0f*aspect,0)); + progCur->UniformMat4(progCur->u_mvpMtx,mtx2D); + font->Print("Next",bNext.x,(0.95-bNext.y)/aspect,0.5/aspect); + font->Print("Prev",bPrev.x,(0.95-bPrev.y)/aspect,0.5/aspect); + char temp[256]={0}; + if(mdlTest->animations.size){ + snprintf(temp,256,"animation %d/%d: %s\n",animIdx,mdlTest->animations.size,mdlTest->animations[animIdx].name.c_str()); + font->Print(temp,0,0.1/aspect,0.5/aspect); + snprintf(temp,256,"frame %d/%d\n",(int)curFrame,mdlTest->animations[animIdx].frames.size); + font->Print(temp,0,0.15/aspect,0.5/aspect); + } + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(2); + glUseProgram(0); + CheckGLError("Draw", __FILE__, __LINE__); +} + +void skinGame::OnTouch(float tx, float ty, int ta, int tf){ + float x = tx/width; +#ifdef ANDROID + float y = (ty-64)/height; +#else + float y = ty/height; +#endif + if(ta==0){ + bNext.Hit(x,y); + bPrev.Hit(x,y); + joyL.Hit(x,y,tf); + joyM.Hit(x,y,tf); + }else if(ta==1){ + joyL.Release(tf); + joyM.Release(tf); + }else if(ta==2){ + joyL.Move(x,y,tf); + joyM.Move(x,y,tf); + } +} + +void VAOFromModel(Model *mdl, VertexArrayObject *out){ + out->Create(); + out->Bind(); + //TODO separate function for vbo attribs + mdl->vbo.Bind(); + for(int i=0; ivertexFormat.size; i++){ + vertAttrib_t &va = mdl->vertexFormat[i]; + out->SetAttribute(va.id,va.size,va.type,va.norm,va.stride,(void*)va.offset); + } + mdl->vbo.Unbind(); + if(mdl->indexCount){ + mdl->ibo.Bind(); + } + out->Unbind(); +} + +void VAOFromMDL(Model *mdl, VertexArrayObject *out,int a_weight,int a_bones){ + out->Create(); + out->Bind(); + mdl->vbo.Bind(); + + for(int i=0; ivertexFormat.size; i++){ + vertAttrib_t &va = mdl->vertexFormat[i]; + int id = va.id; + if(va.id == 3) + id = a_weight; + else if(va.id == 4) + id = a_bones; + out->SetAttribute(id,va.size,va.type,va.norm,va.stride,(void*)va.offset); + } + mdl->vbo.Unbind(); + if(mdl->indexCount){ + mdl->ibo.Bind(); + } + out->Unbind(); +} + +void MeshFromModelSkeleton(Mesh *mesh, Model *mdl,mat4 *bonesMtx){ + int nv = mdl->skeleton.size*2; + float *verts = new float[nv*3]; + *mesh = Mesh(verts,nv,GL_LINES); + UpdateSkeletonMesh(mesh,mdl,bonesMtx); +} + +void UpdateSkeletonMesh(Mesh *mesh, Model *mdl, mat4 *bonesMtx){ + for(int i=0;iskeleton.size;i++){ + glm::vec3 *v = (glm::vec3*)(mesh->verts+i*6); + v[0] = vec3(bonesMtx[i]*vec4(0,0,0,1)); + int p = mdl->skeleton[i].parent; + v[1] = p!=-1 ? vec3(bonesMtx[p]*vec4(0,0,0,1)) : v[0]; + } +} + +void DrawModel(Model *mdl,VertexArrayObject *vao){ + /* + mdl->vbo.Bind(); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,mdl->vertexStride,0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,mdl->vertexStride,(void*)12); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,mdl->vertexStride,(void*)24); + mdl->vbo.Unbind(); + */ + vao->Bind(); + + for(int i=0; isubmeshes.size; i++){ + if(mdl->materials.size&&mdl->materials[mdl->submeshes[i].mat].mat) + mdl->materials[mdl->submeshes[i].mat].mat->Bind(0); + if(mdl->indexCount){ + //mdl->ibo.Bind(); + glDrawElements(GL_TRIANGLES,mdl->submeshes[i].count,mdl->indexType,(void*)(mdl->submeshes[i].offs*mdl->indexSize)); + //mdl->ibo.Unbind(); + }else{ + glDrawArrays(GL_TRIANGLES, mdl->submeshes[i].offs, mdl->submeshes[i].count); + } + } + //glDrawArrays(GL_POINTS, 0, mdl->vertexCount); + vao->Unbind(); +} + +void DrawMesh(Mesh *mesh){ + mesh->Bind(); + mesh->Draw(); + mesh->Unbind(); +} + +Button::Button(float nx, float ny, float nw, float nh, bool adjust): + x(nx),y(ny),w(nw),h(nh),text(0),active(true),pressed(false) +{ +} +void Button::Update(){} +bool Button::SetUniform(int loc){return false;} diff --git a/demos/skinning/mdl_loader.cpp b/demos/skinning/mdl_loader.cpp new file mode 100644 index 0000000..5b11093 --- /dev/null +++ b/demos/skinning/mdl_loader.cpp @@ -0,0 +1,1061 @@ + +#include "log.h" +#include "mdl_loader.h" +#include +#include +#include "system/FileSystem.h" +#include +#include +#include +#include + +typedef unsigned char byte; +typedef glm::vec2 Vector2D; +typedef glm::vec3 Vector; +typedef glm::quat Quaternion; +typedef glm::vec3 RadianEuler; +typedef glm::mat3x4 matrix3x4_t; + +#define IDSTUDIOHEADER (('T'<<24)+('S'<<16)+('D'<<8)+'I') +#define MAX_NUM_LODS 8 +#define MAX_NUM_BONES_PER_VERT 3 + +struct studiohdr_t{ + uint32_t id; + uint32_t version; + uint32_t checksum; // this has to be the same in the phy and vtx files to load! + inline const char * pszName( void ) const { return name; } + char name[64]; + uint32_t length; + Vector eyeposition; // ideal eye position + Vector illumposition; // illumination center + Vector hull_min; // ideal movement hull size + Vector hull_max; + Vector view_bbmin; // clipping bounding box + Vector view_bbmax; + int flags; + int numbones; // bones + int boneindex; + int numbonecontrollers; // bone controllers + int bonecontrollerindex; + int numhitboxsets; + int hitboxsetindex; + int numlocalanim; // animations/poses + int localanimindex; // animation descriptions + int numlocalseq; // sequences + int localseqindex; + mutable int activitylistversion; // initialization flag - have the sequences been indexed? + mutable int eventsindexed; + int numtextures; + int textureindex; + int numcdtextures; + int cdtextureindex; + int numskinref; + int numskinfamilies; + int skinindex; + int numbodyparts; + int bodypartindex; + int numlocalattachments; + int localattachmentindex; + int numlocalnodes; + int localnodeindex; + int localnodenameindex; + int numflexdesc; + int flexdescindex; + int numflexcontrollers; + int flexcontrollerindex; + int numflexrules; + int flexruleindex; + int numikchains; + int ikchainindex; + int nummouths; + int mouthindex; + int numlocalposeparameters; + int localposeparamindex; + int surfacepropindex; + int keyvalueindex; + int keyvaluesize; + int numlocalikautoplaylocks; + int localikautoplaylockindex; + float mass; + int contents; + int numincludemodels; + int includemodelindex; + //mutable void *virtualModel; + uint32_t virtualModel; + int szanimblocknameindex; + int numanimblocks; + int animblockindex; + uint32_t animblockModel;//void* + int bonetablebynameindex; + //void *pVertexBase; + uint32_t pVertexBase; + //void *pIndexBase; + uint32_t pIndexBase; + byte constdirectionallightdot; + byte rootLOD; + byte numAllowedRootLODs; + byte unused[1]; + int unused4; // zero out if version < 47 + int numflexcontrollerui; + int flexcontrolleruiindex; + int unused3[2]; + int studiohdr2index; + int unused2[1]; + studiohdr_t() {} +private: + // No copy constructors allowed + studiohdr_t(const studiohdr_t& vOther); +}; + +struct mstudio_modelvertexdata_t +{ + // base of external vertex data stores + //const void *pVertexData; + //const void *pTangentData; + uint32_t pVertexData; + uint32_t pTangentData; +}; + +struct mstudio_meshvertexdata_t +{ + //const mstudio_modelvertexdata_t *modelvertexdata; + uint32_t modelvertexdata; + int numLODVertexes[MAX_NUM_LODS]; +}; + +struct mstudiomesh_t +{ + int material; + int modelindex; + int numvertices; // number of unique vertices/normals/texcoords + int vertexoffset; // vertex mstudiovertex_t + int numflexes; // vertex animation + int flexindex; + int materialtype; + int materialparam; + int meshid; + Vector center; + mstudio_meshvertexdata_t vertexdata; + int unused[8]; // remove as appropriate + mstudiomesh_t(){} +private: + // No copy constructors allowed + mstudiomesh_t(const mstudiomesh_t& vOther); +}; + +// studio models +struct mstudiomodel_t +{ + inline const char * pszName( void ) const { return name; } + char name[64]; + int type; + float boundingradius; + int nummeshes; + int meshindex; + int numvertices; // number of unique vertices/normals/texcoords + int vertexindex; // vertex Vector + int tangentsindex; // tangents Vector + int numattachments; + int attachmentindex; + int numeyeballs; + int eyeballindex; + mstudio_modelvertexdata_t vertexdata; + int unused[8]; // remove as appropriate +}; + +struct mstudiobodyparts_t +{ + int sznameindex; + int nummodels; + int base; + int modelindex; // index into models array +}; + +struct mstudiobone_t +{ + int sznameindex; + int parent; // parent bone + int bonecontroller[6]; // bone controller index, -1 == none + Vector pos; + Quaternion quat; + RadianEuler rot; + Vector posscale; + Vector rotscale; + matrix3x4_t poseToBone; + Quaternion qAlignment; + int flags; + int proctype; + int procindex; // procedural rule + mutable int physicsbone; // index into physically simulated bone + int surfacepropidx; // index into string tablefor property name + int contents; // See BSPFlags.h for the contents flags + int unused[8]; // remove as appropriate + mstudiobone_t(){} +private: + // No copy constructors allowed + mstudiobone_t(const mstudiobone_t& vOther); +}; + +// sequence descriptions +struct mstudioseqdesc_t{ + int baseptr; + int szlabelindex; + inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; } + int szactivitynameindex; + inline char * const pszActivityName( void ) const { return ((char *)this) + szactivitynameindex; } + int flags; // looping/non-looping flags + int activity; // initialized at loadtime to game DLL values + int actweight; + int numevents; + int eventindex; +// inline mstudioevent_t *pEvent( int i ) const { Assert( i >= 0 && i < numevents); return (mstudioevent_t *)(((byte *)this) + eventindex) + i; }; + Vector bbmin; // per sequence bounding box + Vector bbmax; + int numblends; + int animindexindex; + int movementindex; // [blend] float array for blended movement + int groupsize[2]; + int paramindex[2]; // X, Y, Z, XR, YR, ZR + float paramstart[2]; // local (0..1) starting value + float paramend[2]; // local (0..1) ending value + int paramparent; + float fadeintime; // ideal cross fate in time (0.2 default) + float fadeouttime; // ideal cross fade out time (0.2 default) + int localentrynode; // transition node at entry + int localexitnode; // transition node at exit + int nodeflags; // transition rules + float entryphase; // used to match entry gait + float exitphase; // used to match exit gait + float lastframe; // frame that should generation EndOfSequence + int nextseq; // auto advancing sequences + int pose; // index of delta animation between end and nextseq + int numikrules; + int numautolayers; // + int autolayerindex; +// inline mstudioautolayer_t *pAutolayer( int i ) const { Assert( i >= 0 && i < numautolayers); return (mstudioautolayer_t *)(((byte *)this) + autolayerindex) + i; }; + int weightlistindex; +// inline float *pBoneweight( int i ) const { return ((float *)(((byte *)this) + weightlistindex) + i); }; +// inline float weight( int i ) const { return *(pBoneweight( i)); }; + // FIXME: make this 2D instead of 2x1D arrays + int posekeyindex; +// float *pPoseKey( int iParam, int iAnim ) const { return (float *)(((byte *)this) + posekeyindex) + iParam * groupsize[0] + iAnim; } +// float poseKey( int iParam, int iAnim ) const { return *(pPoseKey( iParam, iAnim )); } + int numiklocks; + int iklockindex; +// inline mstudioiklock_t *pIKLock( int i ) const { Assert( i >= 0 && i < numiklocks); return (mstudioiklock_t *)(((byte *)this) + iklockindex) + i; }; + // Key values + int keyvalueindex; + int keyvaluesize; +// inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; } + int cycleposeindex; // index of pose parameter to use as cycle index + int unused[7]; // remove/add as appropriate (grow back to 8 ints on version change!) + mstudioseqdesc_t(){} +private: + // No copy constructors allowed + mstudioseqdesc_t(const mstudioseqdesc_t& vOther); +}; + +// animation frames +union mstudioanimvalue_t{ + struct { + byte valid; + byte total; + } num; + short value; +}; + +struct mstudioanim_valueptr_t{ + short offset[3]; + inline mstudioanimvalue_t *pAnimvalue( int i ) const { if (offset[i] > 0) return (mstudioanimvalue_t *)(((byte *)this) + offset[i]); else return NULL; } +}; + +#define STUDIO_ANIM_RAWPOS 0x01 // Vector48 +#define STUDIO_ANIM_RAWROT 0x02 // Quaternion48 +#define STUDIO_ANIM_ANIMPOS 0x04 // mstudioanim_valueptr_t +#define STUDIO_ANIM_ANIMROT 0x08 // mstudioanim_valueptr_t +#define STUDIO_ANIM_DELTA 0x10 +#define STUDIO_ANIM_RAWROT2 0x20 // Quaternion64 + +// per bone per animation DOF and weight pointers +struct mstudioanim_t +{ + byte bone; + byte flags; // weighing options + // valid for animating data only + inline byte *pData( void ) const { return (((byte *)this) + sizeof( struct mstudioanim_t )); } + inline mstudioanim_valueptr_t *pRotV( void ) const { return (mstudioanim_valueptr_t *)(pData()); } + inline mstudioanim_valueptr_t *pPosV( void ) const { return (mstudioanim_valueptr_t *)(pData()) + ((flags & STUDIO_ANIM_ANIMROT) != 0); } + // valid if animation unvaring over timeline + //inline Quaternion48 *pQuat48( void ) const { return (Quaternion48 *)(pData()); } + //inline Quaternion64 *pQuat64( void ) const { return (Quaternion64 *)(pData()); } + //inline Vector48 *pPos( void ) const { return (Vector48 *)(pData() + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof( *pQuat48() ) + ((flags & STUDIO_ANIM_RAWROT2) != 0) * sizeof( *pQuat64() ) ); } + inline glm::quat Quat() const { + struct t{uint64_t x:21; + uint64_t y:21; + uint64_t z:21; + uint64_t wneg:1; + }raw = *((t*)pData()); + glm::quat tmp; + tmp.x = ((int)raw.x - 1048576) * (1 / 1048576.5f); + tmp.y = ((int)raw.y - 1048576) * (1 / 1048576.5f); + tmp.z = ((int)raw.z - 1048576) * (1 / 1048576.5f); + tmp.w = sqrt( 1 - tmp.x * tmp.x - tmp.y * tmp.y - tmp.z * tmp.z ); + if (raw.wneg) + tmp.w = -tmp.w; + return tmp; + } + inline glm::vec3 PosVec3() const { + int ofs = ((flags & STUDIO_ANIM_RAWROT2) != 0) * 8 + ((flags & STUDIO_ANIM_RAWROT) != 0) * 6; + return glm::vec3(glm::unpackHalf1x16(*(short*)(pData()+ofs)),glm::unpackHalf1x16(*(short*)(pData()+ofs+2)),glm::unpackHalf1x16(*(short*)(pData()+ofs+4))); + } + short nextoffset; + inline mstudioanim_t *pNext( void ) const { if (nextoffset != 0) return (mstudioanim_t *)(((byte *)this) + nextoffset); else return NULL; } +}; + +struct mstudioanimdesc_t{ + int baseptr; + //inline studiohdr_t *pStudiohdr( void ) const { return (studiohdr_t *)(((byte *)this) + baseptr); } + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + float fps; // frames per second + int flags; // looping/non-looping flags + int numframes; + // piecewise movement + int nummovements; + int movementindex; + //inline mstudiomovement_t * const pMovement( int i ) const { return (mstudiomovement_t *)(((byte *)this) + movementindex) + i; }; + int unused1[6]; // remove as appropriate (and zero if loading older versions) + int animblock; + int animindex; // non-zero when anim data isn't in sections + //mstudioanim_t *pAnimBlock( int block, int index ) const; // returns pointer to a specific anim block (local or external) + //mstudioanim_t *pAnim( int *piFrame, float &flStall ) const; // returns pointer to data and new frame index + //mstudioanim_t *pAnim( int *piFrame ) const; // returns pointer to data and new frame index + int numikrules; + int ikruleindex; // non-zero when IK data is stored in the mdl + int animblockikruleindex; // non-zero when IK data is stored in animblock file + //mstudioikrule_t *pIKRule( int i ) const; + int numlocalhierarchy; + int localhierarchyindex; + //mstudiolocalhierarchy_t *pHierarchy( int i ) const; + int sectionindex; + int sectionframes; // number of frames used in each fast lookup section, zero if not used + //inline mstudioanimsections_t * const pSection( int i ) const { return (mstudioanimsections_t *)(((byte *)this) + sectionindex) + i; } + short zeroframespan; // frames per span + short zeroframecount; // number of spans + int zeroframeindex; + //byte *pZeroFrameData( ) const { if (zeroframeindex) return (((byte *)this) + zeroframeindex); else return NULL; }; + mutable float zeroframestalltime; // saved during read stalls + mstudioanimdesc_t(){} +private: + // No copy constructors allowed + mstudioanimdesc_t(const mstudioanimdesc_t& vOther); +}; + +struct mstudiohitboxset_t{ + int sznameindex; + //inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int numhitboxes; + int hitboxindex; + //inline mstudiobbox_t *pHitbox( int i ) const { return (mstudiobbox_t *)(((byte *)this) + hitboxindex) + i; }; +}; + +struct mstudiobbox_t{ + int bone; + int group; // intersection group + Vector bbmin; // bounding box + Vector bbmax; + int szhitboxnameindex; // offset to the name of the hitbox. + int unused[8]; + mstudiobbox_t() {} +private: + // No copy constructors allowed + mstudiobbox_t(const mstudiobbox_t& vOther); +}; + +// skin info +struct mstudiotexture_t{ + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int flags; + int used; + int unused1; + //mutable IMaterial *material; // fixme: this needs to go away . .isn't used by the engine, but is used by studiomdl + uint32_t material; + //mutable void *clientmaterial; // gary, replace with client material pointer if used + uint32_t clientmaterial; + int unused[10]; +}; + +// demand loaded sequence groups +struct mstudiomodelgroup_t{ + int szlabelindex; // textual name + inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; } + int sznameindex; // file name + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } +}; + +//vvd +struct mstudioboneweight_t +{ + float weight[MAX_NUM_BONES_PER_VERT]; + char bone[MAX_NUM_BONES_PER_VERT]; + byte numbones; +}; + +// NOTE: This is exactly 48 bytes +struct mstudiovertex_t +{ + mstudioboneweight_t m_BoneWeights; + Vector m_vecPosition; + Vector m_vecNormal; + Vector2D m_vecTexCoord; + mstudiovertex_t() {} +private: + // No copy constructors allowed + mstudiovertex_t(const mstudiovertex_t& vOther); +}; + +struct vertexFileHeader_t +{ + int id; // MODEL_VERTEX_FILE_ID + int version; // MODEL_VERTEX_FILE_VERSION + uint32_t checksum; // same as studiohdr_t, ensures sync + int numLODs; // num of valid lods + int numLODVertexes[MAX_NUM_LODS]; // num verts for desired root lod + int numFixups; // num of vertexFileFixup_t + int fixupTableStart; // offset from base to fixup table + int vertexDataStart; // offset from base to vertex block + int tangentDataStart; // offset from base to tangent block +}; + +// apply sequentially to lod sorted vertex and tangent pools to re-establish mesh order +struct vertexFileFixup_t +{ + int lod; // used to skip culled root lod + int sourceVertexID; // absolute index from start of vertex/tangent blocks + int numVertexes; +}; + +//vtx +#pragma pack(1) +struct vtxFileHeader_t +{ + int version; + int vertCacheSize; + unsigned short maxBonesPerStrip; + unsigned short maxBonesPerTri; + int maxBonesPerVert; + uint32_t checkSum; + int numLODs; // garymcthack - this is also specified in ModelHeader_t and should match + int materialReplacementListOffset; + int numBodyParts; + int bodyPartOffset; +}; + +struct vtxBodyPartHeader_t +{ + int numModels; + int modelOffset; +}; + +// This maps one to one with models in the mdl file. +// There are a bunch of model LODs stored inside potentially due to the qc $lod command +struct vtxModelHeader_t +{ + int numLODs; // garymcthack - this is also specified in FileHeader_t + int lodOffset; +}; + +struct vtxModelLODHeader_t +{ + int numMeshes; + int meshOffset; + float switchPoint; +}; + +// a collection of locking groups: +// up to 4: +// non-flexed, hardware skinned +// flexed, hardware skinned +// non-flexed, software skinned +// flexed, software skinned +// +// A mesh has a material associated with it. +struct vtxMeshHeader_t +{ + int numStripGroups; + int stripGroupHeaderOffset; + unsigned char flags; +}; + +// a locking group +// a single vertex buffer +// a single index buffer +struct vtxStripGroupHeader_t +{ + // These are the arrays of all verts and indices for this mesh. strips index into this. + int numVerts; + int vertOffset; + int numIndices; + int indexOffset; + int numStrips; + int stripOffset; + unsigned char flags; +}; + +struct vtxVertex_t +{ + // these index into the mesh's vert[origMeshVertID]'s bones + unsigned char boneWeightIndex[MAX_NUM_BONES_PER_VERT]; + unsigned char numBones; + unsigned short origMeshVertID; + // for sw skinned verts, these are indices into the global list of bones + // for hw skinned verts, these are hardware bone indices + char boneID[MAX_NUM_BONES_PER_VERT]; +}; +#pragma pack() + +Model *MDLLoader::Load(const char *name){ + Log("Loading model: %s\n", name); + std::string filePath = name; + loadMDLOut_t mdlData; + if(!LoadMDL(filePath.c_str(),mdlData)) + return NULL; + int ext = filePath.find(".mdl"); + filePath = filePath.substr(0,ext); + + Model *mdl = new Model(); + int lod = 0; + LoadVVD((filePath+".vvd").c_str(),mdl,lod); + filePath+=".vtx"; + if(!g_fs.FileExists(filePath.c_str())) + filePath = filePath.substr(0,ext)+".dx90.vtx"; + LoadVTX(filePath.c_str(),mdl,mdlData,lod); + + mdl->vbo.Create(); + mdl->vbo.Upload(mdl->vertexFormat[0].stride*mdl->vertexCount,mdl->verts); + mdl->ibo.Create(); + mdl->ibo.Upload(mdl->indexSize*mdl->indexCount,mdl->inds); + + mdl->submeshes.Resize(mdlData.numMeshes); + for(int i=0;isubmeshes[i]={(uint32_t)mdlData.meshes[i].indOffs,mdlData.meshes[i].indCount,i}; + } + + mdl->skeleton.Resize(mdlData.bones.size); + for(int i=0;iskeleton[i] = mdlData.bones[i]; + } + + mdl->animations.Resize(mdlData.anims.size); + for(int i=0;ianimations[i].name = mdlData.anims[i].name; + mdl->animations[i].fps = mdlData.anims[i].fps; + mdl->animations[i].frames.Resize(mdlData.anims[i].frames.size); + for(int f=0;fanimations[i].frames[f].bones.Resize(mdlData.anims[i].frames[f].bones.size); + for(int b=0;banimations[i].frames[f].bones[b] = mdlData.anims[i].frames[f].bones[b]; + } + } + } + + return mdl; +} + +bool MDLLoader::CheckExt(const char *name){ + return strstr(name,".mdl")!=0; +} + +#if 1 +bool MDLLoader::LoadMDL(const char *name, loadMDLOut_t &mdlData){ + IFile *mdlfile = g_fs.Open(name); + if(!mdlfile){ + return false; + } + + uint32_t fileLen = mdlfile->GetLen(); + Log("MDL: file len %d\n",fileLen); + char *data = new char[fileLen]; + mdlfile->Seek(0); + mdlfile->Read(data,fileLen); + g_fs.Close(mdlfile); + studiohdr_t *mdlHeader = (studiohdr_t*)data; + + if(mdlHeader->id != IDSTUDIOHEADER){ + Log("MDL: wrongh id(%X, must be %X)\n",mdlHeader->id,IDSTUDIOHEADER); + return false; + } + if(mdlHeader->length != fileLen){ + Log("MDL: wrongh length(%d)\n",mdlHeader->length); + return false; + } + + if(mdlHeader->numbodyparts != 1) + Log("MDL: %s num bodyparts %d\n", name, mdlHeader->numbodyparts); + + Log("MDL: numincludemodels %d\n",mdlHeader->numincludemodels); + if(mdlHeader->numincludemodels){ + mstudiomodelgroup_t *incs = (mstudiomodelgroup_t*)(data+mdlHeader->includemodelindex); + for(int i=0; inumincludemodels; i++){ + Log( "include model %d: label %s, name %s\n",i,incs[i].pszLabel(),incs[i].pszName()); + } + } + if(mdlHeader->szanimblocknameindex) + Log("anim block name %s\n",data+mdlHeader->szanimblocknameindex); + + Log("MDL: num bones %d\n",mdlHeader->numbones); + mdlData.bones.Resize(mdlHeader->numbones); + mstudiobone_t *bones = (mstudiobone_t*)(data+mdlHeader->boneindex); + for(int i=0; inumbones; i++){ + mstudiobone_t &b = bones[i]; + char *bonename = data+mdlHeader->boneindex+sizeof(mstudiobone_t)*i+b.sznameindex; + Log( "bone %d: name %s, parent %d, pos (%.3f,%.3f,%.3f), rot(%.3f,%.3f,%.3f)\n",i,bonename,b.parent,b.pos.x,b.pos.y,b.pos.z, b.rot.x,b.rot.y,b.rot.z); + mdlData.bones[i]={b.parent,b.pos,b.quat}; + } + + Log("MDL: numtextures %d, numcdtextures %d\n",mdlHeader->numtextures,mdlHeader->numcdtextures); + mstudiotexture_t *texs = (mstudiotexture_t*)(data+mdlHeader->textureindex); + for(int i=0; inumtextures; i++){ + Log("tex %d: %s\n",i,texs[i].pszName()); + } + + Log("MDL: numlocalanim %d, numlocalseq %d, numanimblocks %d\n",mdlHeader->numlocalanim,mdlHeader->numlocalseq,mdlHeader->numanimblocks); + if(mdlHeader->numlocalseq){ + mstudioseqdesc_t *seqs = (mstudioseqdesc_t*)(data+mdlHeader->localseqindex); + for(int i=0; inumlocalseq; i++){ + Log( "seq %d: label %s, activity name %s\n",i,seqs[i].pszLabel(),seqs[i].pszActivityName()); + } + } + if(mdlHeader->numlocalanim){ + mstudioanimdesc_t *anims = (mstudioanimdesc_t*)(data+mdlHeader->localanimindex); + mdlData.anims.Resize(mdlHeader->numlocalanim); + for(int i=0; inumlocalanim; i++){ + mstudioanimdesc_t &ad=anims[i]; + Log( "animdesc %d: label %s, num frames %d, fps %.3f, flags %d, block %d, index %d, section frames %d\n",i,ad.pszName(),ad.numframes,ad.fps,ad.flags,ad.animblock,ad.animindex,ad.sectionframes); + //if(i<4) + { + mdlData.anims[i].name = ad.pszName(); + mdlData.anims[i].fps = ad.fps; + mdlData.anims[i].frames.Resize(ad.numframes); + + for(int f=0;fnumbones); + } + mstudioanim_t *pAnim = (mstudioanim_t*)((char*)(anims+i)+anims[i].animindex); + for(int b=0;bnumbones;b++){ + for(int f=0;fbone==b){ + //if(f==0/* && i==1*/) Log("anim %p: bone %d, flags %d, next %d\n",pAnim,pAnim->bone,pAnim->flags,pAnim->nextoffset); + if(pAnim->flags&STUDIO_ANIM_RAWPOS){ + bone.pos = pAnim->PosVec3(); + } + //TODO: STUDIO_ANIM_RAWROT + if(pAnim->flags&STUDIO_ANIM_RAWROT2) + bone.rot = pAnim->Quat(); + if(pAnim->flags&STUDIO_ANIM_ANIMROT){ + vec3 animAng=bones[b].rot; + mstudioanim_valueptr_t *rotV = pAnim->pRotV(); + for(int j=0;j<3;j++){ + mstudioanimvalue_t *animVal = rotV->pAnimvalue(j); + if(animVal){ + int k = glm::min(f,animVal->num.valid-1); + animAng[j] += animVal[k+1].value*bones[b].rotscale[j]; + } + } + bone.rot = glm::quat(animAng); + } + if(pAnim->flags&STUDIO_ANIM_ANIMPOS){ + vec3 animPos=bones[b].pos; + mstudioanim_valueptr_t *posV = pAnim->pPosV(); + for(int j=0;j<3;j++){ + mstudioanimvalue_t *animVal = posV->pAnimvalue(j); + if(animVal){ + int k = glm::min(f,animVal->num.valid-1); + //Log("animVal %d: %d num %d %d\n",j,animVal[1].value,animVal->num.total,animVal->num.valid); + animPos[j] += animVal[k+1].value*bones[b].posscale[j]; + } + } + bone.pos = animPos; + } + }else{ + bone = mdlData.bones[b]; + } + + mdlData.anims[i].frames[f].bones[b] = bone; + } + if(pAnim&&pAnim->bone==b) + pAnim = pAnim->pNext(); + } + } + } + } + + mstudiobodyparts_t *bodyparts = (mstudiobodyparts_t*)(data+mdlHeader->bodypartindex); + //for(int bpi = 0; bpinumbodyparts; bpi++) + int bpi = 0; + { + mstudiobodyparts_t &bp = bodyparts[bpi]; + if(bp.nummodels!=1) + Log("MDL: %s bodyparts[%d] num models %d\n", name, bpi, bp.nummodels); + + mstudiomodel_t *bpmodels = (mstudiomodel_t*)((char*)(bodyparts+bpi)+bp.modelindex); + + //for(int mi = 0;miRead(&mdlHeader,sizeof(studiohdr_t)); + + if(mdlHeader.id != IDSTUDIOHEADER) + return false; + + if(mdlHeader.numbodyparts != 1) + Log("MDL: %s num bodyparts %d\n", name, mdlHeader.numbodyparts); + + Log("MDL: num bones %d\n",mdlHeader.numbones); + mdlData.bones.Resize(mdlHeader.numbones); + mstudiobone_t *bones = new mstudiobone_t[mdlHeader.numbones]; + mdlfile->Seek(mdlHeader.boneindex); + mdlfile->Read(bones,sizeof(mstudiobone_t)*mdlHeader.numbones); + for(int b=0; bSeek(mdlHeader.boneindex+sizeof(mstudiobone_t)*b+bones[b].sznameindex); + mdlfile->GetString(bonename,64); + Log( "bone %d: name %s, parent %d, pos (%f,%f,%f), rot(%f,%f,%f)\n",b,bonename,bones[b].parent,bones[b].pos.x,bones[b].pos.y,bones[b].pos.z, bones[b].rot.x,bones[b].rot.y,bones[b].rot.z); + */ + mdlData.bones[b]={bones[b].parent,bones[b].pos,bones[b].quat}; + } + delete[] bones; + + Log("MDL: numhitboxsets %d\n",mdlHeader.numhitboxsets); + if(mdlHeader.numhitboxsets){ + mstudiohitboxset_t *hbsets = new mstudiohitboxset_t[mdlHeader.numhitboxsets]; + mdlfile->Seek(mdlHeader.hitboxsetindex); + mdlfile->Read(hbsets,sizeof(mstudiohitboxset_t)*mdlHeader.numhitboxsets); + for(int i=0; iSeek(mdlHeader.hitboxsetindex+sizeof(mstudiohitboxset_t)*i+hbsets[i].sznameindex); + mdlfile->GetString(temp,64); + Log("hitbox std %d: %s num %d\n",i,temp,hbsets[i].numhitboxes); + } + + mstudiobbox_t *hitboxes = new mstudiobbox_t[hbsets[0].numhitboxes]; + mdlfile->Seek(mdlHeader.hitboxsetindex+hbsets[0].hitboxindex); + mdlfile->Read(hitboxes,sizeof(mstudiobbox_t)*hbsets[0].numhitboxes); + for(int i=0;iSeek(mdlHeader.hitboxsetindex+hbsets[0].hitboxindex+of); + mdlfile->GetString(temp,64); + } + Log("bbox %d: %s(%d) bone %d, min(%.2f,%.2f,%.2f) max(%.2f,%.2f,%.2f)\n",i,temp,of,hitboxes[i].bone,hitboxes[i].bbmin.x,hitboxes[i].bbmin.y,hitboxes[i].bbmin.z,hitboxes[i].bbmax.x,hitboxes[i].bbmax.y,hitboxes[i].bbmax.z); + } + delete[] hitboxes; + delete[] hbsets; + } + + Log("MDL: numlocalanim %d, numlocalseq %d\n",mdlHeader.numlocalanim,mdlHeader.numlocalseq); + if(mdlHeader.numlocalseq){ + mstudioseqdesc_t *seqs = new mstudioseqdesc_t[mdlHeader.numlocalseq]; + mdlfile->Seek(mdlHeader.localseqindex); + mdlfile->Read(seqs,sizeof(mstudioseqdesc_t)*mdlHeader.numlocalseq); + for(int i=0; iSeek(mdlHeader.localseqindex+sizeof(mstudioseqdesc_t)*i+seqs[i].szlabelindex); + mdlfile->GetString(temp,64); + mdlfile->Seek(mdlHeader.localseqindex+sizeof(mstudioseqdesc_t)*i+seqs[i].szactivitynameindex); + mdlfile->GetString(temp2,64); + Log( "seq %d: label %s, activity name %s\n",i,temp,temp2); + } + delete[] seqs; + } + if(mdlHeader.numlocalanim){ + mstudioanimdesc_t *anims = new mstudioanimdesc_t[mdlHeader.numlocalanim]; + mdlfile->Seek(mdlHeader.localanimindex); + mdlfile->Read(anims,sizeof(mstudioanimdesc_t)*mdlHeader.numlocalanim); + for(int i=0; iSeek(mdlHeader.localanimindex+sizeof(mstudioanimdesc_t)*i+ad.sznameindex); + mdlfile->GetString(temp,64); + Log( "animdesc %d: label %s, num franes %d, block %d, index %d, section frames %d\n",i,temp,ad.numframes,ad.animblock,ad.animindex,ad.sectionframes); + } + mstudioanim_t anim0 = {}; + mdlfile->Seek(mdlHeader.localanimindex+anims[0].animindex); + mdlfile->Read(&anim0,sizeof(mstudioanim_t)); + Log("anim 0: bone %d, flags %d, next %d\n",anim0.bone,anim0.flags,anim0.nextoffset); + + delete[] anims; + } + + Log("MDL: numtextures %d, numcdtextures %d\n",mdlHeader.numtextures,mdlHeader.numcdtextures); + + Log("MDL: numincludemodels %d\n",mdlHeader.numincludemodels); + if(mdlHeader.numincludemodels){ + mstudiomodelgroup_t *incs = new mstudiomodelgroup_t[mdlHeader.numincludemodels]; + mdlfile->Seek(mdlHeader.includemodelindex); + mdlfile->Read(incs,sizeof(mstudiomodelgroup_t)*mdlHeader.numincludemodels); + for(int i=0; iSeek(mdlHeader.includemodelindex+sizeof(mstudiomodelgroup_t)*i+incs[i].szlabelindex); + mdlfile->GetString(temp,64); + mdlfile->Seek(mdlHeader.includemodelindex+sizeof(mstudiomodelgroup_t)*i+incs[i].sznameindex); + mdlfile->GetString(temp2,64); + Log( "include model %d: label %s, name %s\n",i,temp,temp2); + } + delete[] incs; + } + + mstudiobodyparts_t *bodyparts = new mstudiobodyparts_t[mdlHeader.numbodyparts]; + mdlfile->Seek(mdlHeader.bodypartindex); + mdlfile->Read(bodyparts,sizeof(mstudiobodyparts_t)*mdlHeader.numbodyparts); + + //for(int bpi = 0; bpiSeek(mdlHeader.bodypartindex+(sizeof(mstudiobodyparts_t)*bpi)+bp.modelindex); + mdlfile->Read(bpmodels,sizeof(mstudiomodel_t)*bp.nummodels); + //for(int mi = 0;miSeek(mdlHeader.bodypartindex+ + (sizeof(mstudiobodyparts_t)*bpi)+bp.modelindex+ + (sizeof(mstudiomodel_t)*mi)+bpm.meshindex); + mdlfile->Read(mdlMeshes,sizeof(mstudiomesh_t)*bpm.nummeshes); + + mdlData.meshes = new loadMDLOut_t::mdlOutMesh_t[bpm.nummeshes]; + for(int mshi = 0; mshiRead(&vvdHeader,sizeof(vertexFileHeader_t)); + //Log( "vvd header: version %d, numLODs %d, numLODVertexes[0] %d numFixups %d\n",vvdHeader.version, vvdHeader.numLODs, vvdHeader.numLODVertexes[0],vvdHeader.numFixups); + if(lod >= vvdHeader.numLODs) + lod = vvdHeader.numLODs-1; + + int numVerts = vvdHeader.numLODVertexes[lod]; + char *verts = new char[sizeof(mstudiovertex_t)*numVerts]; + if(!vvdHeader.numFixups){ + vvdfile->Seek(vvdHeader.vertexDataStart); + vvdfile->Read(verts,sizeof(mstudiovertex_t)*numVerts); + }else{ + //Log("Start fixup vertices %s\n",fileName); + vertexFileFixup_t *fixupTable = new vertexFileFixup_t[vvdHeader.numFixups]; + vvdfile->Seek(vvdHeader.fixupTableStart); + vvdfile->Read(fixupTable,sizeof(vertexFileFixup_t)*vvdHeader.numFixups); + + int target = 0; + for(int f=0; fSeek(vvdHeader.vertexDataStart + fixupTable[f].sourceVertexID*sizeof(mstudiovertex_t)); + vvdfile->Read((verts+target*sizeof(mstudiovertex_t)),fixupTable[f].numVertexes*sizeof(mstudiovertex_t)); + target += fixupTable[f].numVertexes; + } + //Log( "read %d fixup verts\n",target); + delete[] fixupTable; + } + Log( "VVD: read %d verts\n",numVerts); + + mdl->vertexCount = numVerts; + mdl->vertexFormat.Resize(5); + mdl->vertexFormat[0]=vertAttrib_t(0,3,GL_FLOAT,false,sizeof(mstudiovertex_t),16);//pos + mdl->vertexFormat[1]=vertAttrib_t(1,3,GL_FLOAT,false,sizeof(mstudiovertex_t),28);//norm + mdl->vertexFormat[2]=vertAttrib_t(2,2,GL_FLOAT,false,sizeof(mstudiovertex_t),40);//uv + mdl->vertexFormat[3]=vertAttrib_t(3,3,GL_FLOAT,false,sizeof(mstudiovertex_t),0);//weight + mdl->vertexFormat[4]=vertAttrib_t(4,4,GL_UNSIGNED_BYTE,false,sizeof(mstudiovertex_t),12);//bone ids + mdl->verts = verts; + + g_fs.Close(vvdfile); + return true; +} + +int CalcVTXIndsCount(vtxFileHeader_t *vtxHeader, int lod){ + int numInds = 0; + + //for(int bpi=0; bpinumBodyParts; pbi++) + int bpi = 0; + { + vtxBodyPartHeader_t *bp = (vtxBodyPartHeader_t*)((char*)vtxHeader+vtxHeader->bodyPartOffset)+bpi; + //for(int mi=0; minumModels; mi++) + int mi = 0; + { + vtxModelHeader_t *model = (vtxModelHeader_t*)(((char*)bp)+bp->modelOffset)+mi; + + vtxModelLODHeader_t *lodHdr = (vtxModelLODHeader_t*)(((char*)model)+model->lodOffset)+lod; + + for(int mshi=0; mshinumMeshes; mshi++) + { + vtxMeshHeader_t *msh = (vtxMeshHeader_t*)(((char*)lodHdr)+lodHdr->meshOffset)+mshi; + //if(msh->numStripGroups!=1) + // Log("VTX: mesh %d numStripGroups %d\n",mshi,msh->numStripGroups); + for(int sgi=0; sginumStripGroups; sgi++) + //int sgi=0; + { + vtxStripGroupHeader_t *sg = ((vtxStripGroupHeader_t*)(((char*)msh)+msh->stripGroupHeaderOffset))+sgi; + + numInds += sg->numIndices; + } + } + } + } + + return numInds; +} + +bool MDLLoader::LoadVTX(const char *name, Model *mdl, loadMDLOut_t &mdlData, int lod){ + IFile *vtxfile = g_fs.Open(name); + if(!vtxfile){ + return false; + } + + char *data = new char[vtxfile->GetLen()]; + vtxfile->Seek(0); + vtxfile->Read(data,vtxfile->GetLen()); + g_fs.Close(vtxfile); + + char *inds = NULL; + int numInds = 0; + + vtxFileHeader_t *vtxHeader = (vtxFileHeader_t*)data; + //Log( "vtx header: version %d, numLODs %d, bodyparts num %d offset 0x%x\n",vtxHeader.version, vtxHeader.numLODs, vtxHeader.numBodyParts, vtxHeader.bodyPartOffset); + + numInds = CalcVTXIndsCount(vtxHeader, lod); + Log("VTX: inds num %d\n",numInds); + inds = new char[numInds*sizeof(uint32_t)]; + + int curInd = 0; + + //for(int bpi=0; bpinumBodyParts; pbi++) + int bpi = 0; + { + vtxBodyPartHeader_t *bp = (vtxBodyPartHeader_t*)(data+vtxHeader->bodyPartOffset)+bpi; + //for(int mi=0; minumModels; mi++) + int mi = 0; + { + vtxModelHeader_t *model = (vtxModelHeader_t*)(((char*)bp)+bp->modelOffset)+mi; + + vtxModelLODHeader_t *lodHdr = (vtxModelLODHeader_t*)(((char*)model)+model->lodOffset)+lod; + + for(int mshi=0; mshinumMeshes; mshi++) + //int mshi=0; + { + vtxMeshHeader_t *msh = (vtxMeshHeader_t*)(((char*)lodHdr)+lodHdr->meshOffset)+mshi; + //Log("VTX: mesh %d: numStripGroups %d, flags %d\n",mshi,msh->numStripGroups,msh->flags); + + mdlData.meshes[mshi].indOffs = curInd; + mdlData.meshes[mshi].indCount = 0; + for(int sgi=0; sginumStripGroups; sgi++) + //int sgi=0; + { + vtxStripGroupHeader_t *sg = ((vtxStripGroupHeader_t*)(((char*)msh)+msh->stripGroupHeaderOffset))+sgi; + + //Log( " vtx strip group %d: Verts num %d offset 0x%x, Indices num %d offset 0x%x, Strips num %d offset 0x%x, flags %d\n",sgi,sg->numVerts,sg->vertOffset,sg->numIndices,sg->indexOffset,sg->numStrips,sg->stripOffset,sg->flags); + vtxVertex_t *vtxVerts = (vtxVertex_t*)(((char*)sg)+sg->vertOffset); + uint16_t *vtxInds = (uint16_t*)(((char*)sg)+sg->indexOffset); + + for(int ind=0; indnumIndices; ind++){ + //int vertOffs = ((mstudiomesh_t*)((mstudiomodel_t*)bodyparts[bph].modelindex)[mh].meshindex)[msh].vertexoffset; + int vertOffs = mdlData.meshes[mshi].vertOffs; + ((uint32_t*)inds)[curInd+ind] = vertOffs + vtxVerts[vtxInds[ind]].origMeshVertID; + } + curInd += sg->numIndices; + mdlData.meshes[mshi].indCount += sg->numIndices; + } + } + } + } + + delete[] data; + + mdl->inds = inds; + mdl->indexCount = numInds; + mdl->indexType = GL_UNSIGNED_INT; + mdl->indexSize = 4; + + return true; +} diff --git a/demos/skinning/mdl_loader.h b/demos/skinning/mdl_loader.h new file mode 100644 index 0000000..dd22bfc --- /dev/null +++ b/demos/skinning/mdl_loader.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "resource/ResourceManager.h" +#include "system/neArray.h" +#include "renderer/Model.h" + +struct loadMDLOut_t{ + loadMDLOut_t(){ + meshes = 0; + } + ~loadMDLOut_t(){ + if(meshes) + delete[] meshes; + } + int numMeshes; + struct mdlOutMesh_t{ + uint64_t vertOffs; + int vertCount; + uint64_t indOffs; + int indCount; + } *meshes; + //int numBones; + neArray bones; + neArray anims; +}; + +class MDLLoader: public IModelLoader{ +public: + MDLLoader(ResourceManager *resMan):IModelLoader(resMan){} + + Model *Load(const char *name); + bool CheckExt(const char *name); + const char *GetExt(){return "mdl";} +private: + bool LoadMDL(const char *name, loadMDLOut_t &mdlData); + bool LoadVVD(const char *name, Model *mdl, int lod); + bool LoadVTX(const char *name, Model *mdl, loadMDLOut_t &mdlData, int lod); +}; + diff --git a/demos/tpsGame/Android.mk b/demos/tpsGame/Android.mk new file mode 100644 index 0000000..a48b7b5 --- /dev/null +++ b/demos/tpsGame/Android.mk @@ -0,0 +1,22 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR = ../nenuzhno-engine/ + +include $(CLEAR_VARS) + +LOCAL_MODULE := nenuzhno-engine +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := . ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)game/IGame.cpp $(ENGINE_DIR)log.cpp $(ENGINE_DIR)system/FileSystem.cpp $(ENGINE_DIR)system/config.cpp \ + $(ENGINE_DIR)cull/frustum.cpp $(ENGINE_DIR)cull/BoundingBox.cpp $(ENGINE_DIR)scene/Scene.cpp \ + $(ENGINE_DIR)graphics/gl_utils.cpp $(ENGINE_DIR)graphics/vbo.cpp $(ENGINE_DIR)graphics/glsl_prog.cpp $(ENGINE_DIR)graphics/texture.cpp $(ENGINE_DIR)graphics/fbo.cpp \ + $(ENGINE_DIR)renderer/renderer.cpp $(ENGINE_DIR)renderer/camera.cpp $(ENGINE_DIR)renderer/Model.cpp $(ENGINE_DIR)renderer/font.cpp $(ENGINE_DIR)renderer/mesh.cpp \ + $(ENGINE_DIR)resource/ResourceManager.cpp $(ENGINE_DIR)resource/vtf_loader.cpp $(ENGINE_DIR)resource/dds_loader.cpp $(ENGINE_DIR)resource/nmf_loader.cpp $(ENGINE_DIR)resource/mesh_loader.cpp \ + init.cpp tpsMaterials.cpp tpsMenu.cpp scene_txt_loader.cpp tpsObjects.cpp tpsPhysics.cpp tpsWeapon.cpp tpsPlayer.cpp + +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lEGL + +LOCAL_C_INCLUDES += ../libs/bullet/src +LOCAL_LDLIBS += -lBullet +LOCAL_LDFLAGS += -L../libs/bullet + +include $(BUILD_SHARED_LIBRARY) diff --git a/demos/tpsGame/InputSystem.h b/demos/tpsGame/InputSystem.h new file mode 100644 index 0000000..5604a5e --- /dev/null +++ b/demos/tpsGame/InputSystem.h @@ -0,0 +1,11 @@ + +#pragma once + +class InputSystem{ +public: + InputSystem(); + void Update(float deltaTime){ + + } +}; + diff --git a/demos/tpsGame/Makefile.mingw b/demos/tpsGame/Makefile.mingw new file mode 100644 index 0000000..e3e0e4a --- /dev/null +++ b/demos/tpsGame/Makefile.mingw @@ -0,0 +1,26 @@ + +ENGINE_DIR = ../.. +TARGET := $(ENGINE_DIR)/assets/tpsGame.exe +LIBS_DIR = ../../../../../Libs +all: $(TARGET) + +INCLUDES = -I. -I$(ENGINE_DIR)/src -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include +CPPFLAGS = -Wall -ggdb -m32 +LDFLAGS = -static-libgcc -static-libstdc++ -ggdb -m32 +LIBS = -lnenuzhno-engine -L$(ENGINE_DIR)/bin -lglfw3 -lglew32 -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw -L$(LIBS_DIR)/gl/glew-2.0.0/lib -lopengl32 -lgdi32 +SRCS = init.cpp scene_txt_loader.cpp tpsMaterials.cpp tpsMenu.cpp tpsPlayer.cpp tpsObjects.cpp tpsWeapon.cpp +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +INCLUDES+= -I$(LIBS_DIR)/bullet3-2.86.1/src +LIBS+= -lBullet -L$(LIBS_DIR)/bullet3-2.86.1/lib +#CPPFLAGS+= -DBULLET +SRCS+= tpsPhysics.cpp + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +$(TARGET): $(OBJS) $(ENGINE_DIR)/bin/libnenuzhno-engine.a + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) + +clean: + del $(subst /,\,$(OBJS)) diff --git a/demos/tpsGame/TODO.txt b/demos/tpsGame/TODO.txt new file mode 100644 index 0000000..604d2ea --- /dev/null +++ b/demos/tpsGame/TODO.txt @@ -0,0 +1,15 @@ +menu +triggers +sounds +characters + +Weapons upgrade +Îñíîâà: ñëîòû, âåñ, (ýíåðãèÿ?) +Áëîê ïèòàíèÿ? +Ïðèöåë +Ðóêîÿòü, ïðèêëàä +Öåëå-óêàçàòåëü, ôîíàðü +Ãëóøèòåëü +Çàòâîð +Ñòâîë + diff --git a/demos/tpsGame/init.cpp b/demos/tpsGame/init.cpp new file mode 100644 index 0000000..d59586f --- /dev/null +++ b/demos/tpsGame/init.cpp @@ -0,0 +1,483 @@ + +#include "log.h" +#include "engine.h" +#include "game/IGame.h" +#include "graphics/gl_utils.h" +#include "graphics/texture.h" +#include "renderer/renderer.h" +#include "renderer/Model.h" +#include "renderer/camera.h" +#include "resource/ResourceManager.h" +#include "system/FileSystem.h" +#include "button.h" +#include "system/config.h" + +#include "tpsMaterials.h" +#include "tpsPlayer.h" +#include "tpsPhysics.h" +#include "tpsObjects.h" +#include "tpsMenu.h" + +using namespace std; + +using glm::vec2; +using glm::vec3; +using glm::mat4; +using glm::translate; +using glm::scale; +using glm::dot; +using glm::normalize; +using glm::mix; +using glm::radians; + +Scene *LoadTXTScene(const char *fileName,ResourceManager *resMan,PhysicsSystem *physics); +Texture *GenerateStarBox(int size, int count); + +std::string strGameLog; + +glm::vec3 atovec3(const char *str) +{ + glm::vec3 out(0); + sscanf(str, "%f %f %f", &out.x, &out.y, &out.z); + return out; +} + +void GameLog(const char *s){ + Log("Game: %s",s); + strGameLog+=s; +} + +//TODO interacting objects +std::vector g_buttons; +std::vector g_projs; + +enum eGameState{ + eGameMenu=1, + eGamePlay=2, + eGamePause=3 +}; + +class tpsGame:public IGame +{ + void Created(); + void Changed(int w, int h); + void Draw(); + const char *GetGamedir(){ + return "tpsGame"; + } + void OnKey(int key, int scancode, int action, int mods); + void OnTouch(float tx, float ty, int ta, int tf); + void OnMouseMove(float x, float y); + + void Update(float dt); + void InitTouch(); + void UpdateMouse(float deltaTime); + void StartGame(); + void ResumeGame(); + void PauseGame(); + + eGameState gameState; + + ResourceManager *resMan; + IRenderer *renderer; + PhysicsSystem *physics; + Scene *scene; + Texture *starBox; + Camera cam; + + double oldTime; + Player *player; + Joystick joyM; + Joystick joyL; + KeyJoystick keyM; + vec2 mousePos; + vec2 lastMousePos; + Button bJump; + Button bUse; + Button bAttack; + Button bPause; + + StaticModel *box1; + //ButtonObject *button1; + //DoorObject *door1; + ButtonObject *curButt; + LightObject *lamp1; + + Weapon pistol; + vec3 pistOffs; + + tpsMenu menu; + tpsSettings settings; + float lastScreenScale; + std::string startScene; + + friend void tpsStartGame(); + friend void tpsResumeGame(); + friend void ShootBullet(vec3 orig, vec3 dir); +}; + +tpsGame *g_game; + +IGame *CreateGame() +{ + return (g_game=new tpsGame()); +} + +Model *g_mdlBox=0; + +void ShootBullet(vec3 orig, vec3 dir) +{ + Projectile *p = new Projectile(orig,dir); + + g_projs.push_back(p); + g_game->scene->AddObject(p); +} + +void tpsGame::InitTouch() +{ + joyM = Joystick(0,0.3,0.5,0.7); + joyL = Joystick(0.5,0,0.5,1); + bJump = Button(0.7,0.5,0.2,0.2); + bUse = Button(0.15,0.25,0.2,0.2); + bAttack = Button(0.75,0.25,0.2,0.2,true); + bPause = Button(0.9,0.1,0.1,0.1); +} + +void tpsStartGame() +{ + g_game->StartGame(); +} + +void tpsResumeGame() +{ + g_game->ResumeGame(); +} + +void tpsGame::ResumeGame() +{ + gameState = eGamePlay; + + //InitTouch(); + renderer->SetScene(scene); + EnableCursor(false); +} + +void tpsGame::PauseGame() +{ + gameState = eGamePause; + renderer->SetScene(0); + EnableCursor(true); +} + +void tpsGame::StartGame() +{ + gameState = eGamePlay; + + Model *mdlBox = resMan->GetModel("cube.nmf"); + g_mdlBox = mdlBox; + + scene = LoadTXTScene(startScene.c_str(),resMan,physics); + starBox = GenerateStarBox(256,128); + scene->skyBox = starBox; + + InitTouch(); + + Model *mdlChar = resMan->GetModel("char1.nmf"); + player = new Player(&cam,scene->startPos,scene->startRot,mdlChar); +#ifdef ANDROID + player->move = &joyM; +#else + keyM = KeyJoystick(IN_KEY_W,IN_KEY_S,IN_KEY_D,IN_KEY_A); + player->move = &keyM; +#endif + player->look = &joyL; + player->jump = &bJump; + player->bAttack = &bAttack; + player->phys = physics->CreatePlayer(1,0.4,player->modelMtx); + scene->AddObject(player); + + box1 = new StaticModel(); + box1->mdl = mdlBox; + box1->modelMtx = translate(mat4(1.0f),vec3(1.0f,6.0f,-3.0f)); + physics->AddBox(vec3(1),box1,100); + scene->AddObject(box1); + +/* + door1 = new DoorObject(vec3(-3.5,1,3.5),mdlBox); + scene->AddObject(door1); + + button1 = new ButtonObject(vec3(-2,1.5f,3.0f)); + button1->mdl = mdlBox; + button1->target = door1; + button1->name = "Button1"; + button1->text = "Open door"; + scene->AddObject(button1); +*/ + //lamp1 = new LightObject(ePoint,vec3(-20.4,1.6,3.5),vec3(0.8,0.8,1),5); + //scene->AddLight(lamp1); + + Model *pistolMdl = resMan->GetModel("pistol1.nmf"); + + pistol = Weapon(eWeapPistol,pistolMdl,5,2); + pistol.offsetMtx = rotate(translate(mat4(1),pistOffs),radians(90.0f),vec3(0,1,0)); + player->weapon = &pistol; + + curButt = 0; + + renderer->SetScene(scene); + EnableCursor(false); +} + +void tpsGame::Created() +{ + gameState = eGameMenu; + resMan = new ResourceManager(); + resMan->Init(); + resMan->AddMaterialLoader(new TXTMaterialLoader(resMan)); + + renderer = CreateRenderer(); + renderer->Init(RENDERER_GUI|RENDERER_LIGHT|RENDERER_BACKBUFFER,resMan); + + ConfigFile config; + config.Load("tpsConfig.txt"); + settings.Load(config); + /*for(uint32_t i=0;i::iterator it = config.values.begin()+i; + Log("cfg %d: %s=%s\n",i,it.first->c_str(),it.second->c_str()); + }*/ + renderer->debug = settings.drawBbox; + lastScreenScale=settings.screenScale; + renderer->SetBackBufferScale(settings.screenScale); + startScene = config.values["startScene"]; + pistOffs = atovec3(config.values["pistOffs"].c_str()); + + physics = new PhysicsSystem(); + physics->Init(settings.debugPhysics?renderer:0); + + menu.Init(&settings,renderer); + //StartGame(); + + mousePos = vec2(0); + lastMousePos = vec2(0); + + Log("tpsGame Init %f\n",GetTime()); + oldTime = GetTime(); +} + +void tpsGame::Changed(int w, int h) +{ + renderer->Resize(w,h); + cam.UpdateProj(80.0f,renderer->aspect,0.05f,100.0f); + renderer->SetCamera(&cam); + menu.Resize(renderer->aspect); + InitTouch(); +} + +void tpsGame::Update(float deltaTime){ + menu.Update(deltaTime); + if(gameState==eGamePlay){ + //TODO fixed update +#ifndef ANDROID + float mouseSens=0.2f; + joyL.vel = (mousePos-lastMousePos)/vec2(renderer->width,renderer->height)*mouseSens; + lastMousePos = mousePos; +#endif + if(lastScreenScale!=settings.screenScale){ + lastScreenScale=settings.screenScale; + renderer->SetBackBufferScale(settings.screenScale); + } + + player->tps = settings.tps; + + physics->Update(deltaTime); + scene->Update(deltaTime); + + if(bPause.pressed){ + bPause.pressed = false; + PauseGame(); + } + + curButt = 0; + float curButtDist=99; + for(uint32_t i=0; irot=player->rot.y; + if(glm::distance(g_buttons[i]->pos,player->cam->pos)>2.0f) + continue; + float buttDist = dot(normalize(g_buttons[i]->pos-player->cam->pos),player->view); + if(buttDist>0.9){ + if(buttDistPress(player); + } + } +} + +void tpsGame::Draw(){ + double startTime = GetTime(); + float deltaTime = (startTime-oldTime); + oldTime = startTime; + + Update(deltaTime); + + renderer->Draw(); + + if(gameState==eGamePlay){ + //if(player->phys->onGround()) + // renderer->DrawText("on ground",0.02,0.1,0.4); + + if(curButt){ + renderer->DrawText(curButt->text.c_str(),0.4*renderer->aspect,0.9,0.5); + } + + //touch + if(settings.drawTouch){ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + renderer->SetColor(1,1,1,0.2); + Button *b = &bJump; + renderer->DrawRect(b->x,b->y,b->w,b->h); + if(curButt){ + b = &bUse; + renderer->DrawRect(b->x,b->y,b->w,b->h); + } + if(player->weapon){ + b = &bAttack; + renderer->DrawRect(b->x,b->y,b->w,b->h); + } + b = &bPause; + renderer->DrawRect(b->x,b->y,b->w,b->h); + glDisable(GL_BLEND); + renderer->SetColor(1,1,1,1); + } + + if(settings.debugPhysics){ + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + physics->world->debugDrawWorld(); + renderer->SetColor(1,1,1,1); + //glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + } + }else{ + menu.Draw(renderer); + } + renderer->Set2DMode(); + //renderer->SetColor(1,1,1,1); + renderer->DrawText(strGameLog.c_str(),0,0.95,0.2); + + CheckGLError("tpsGame::Draw", __FILE__, __LINE__); +} + +Texture *GenerateStarBox(int size, int count){ + GLubyte *starboxData = new GLubyte[size*size]; + memset(starboxData,0,size*size); + /*starboxData[75]=255; + starboxData[130]=255; + starboxData[406]=255; + starboxData[625]=255; + starboxData[1307]=255; + starboxData[3506]=255; + starboxData[4002]=255; + starboxData[2383]=255;*/ + for(int i=0;itarget = GL_TEXTURE_CUBE_MAP; + tex->Create(size,size); + tex->SetFilter(GL_LINEAR, GL_LINEAR); + for(int i=0; i<6; i++){ + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL_LUMINANCE, size,size, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, starboxData); + } + delete[] starboxData; + + return tex; +} + +Button::Button(float nx, float ny, float nw, float nh,bool adj): + x(nx),y(ny),w(nw),h(nh),type(0),text(0),active(true),pressed(false){ +} +Button::Button(float nx, float ny, float nw, float nh, const char *t,bool adj): + x(nx),y(ny),w(nw),h(nh),type(0),text(t),active(true),pressed(false){} +bool Button::SetUniform(int loc){return false;} +void Button::Update(){} + +void tpsGame::OnTouch(float x, float y, int ta, int tf){ + float tx = x/renderer->width; +#ifdef ANDROID + float ty = (y-64)/renderer->height; +#else + float ty = y/renderer->height; +#endif + if(gameState==eGameMenu||gameState==eGamePause){ + menu.OnTouch(tx,ty,ta,tf); + } +#ifdef ANDROID + else if(gameState==eGamePlay){ + if(ta==IN_PRESS){ + joyM.Hit(tx,ty,tf); + joyL.Hit(tx,ty,tf); + bJump.Hit(tx,ty); + bUse.Hit(tx,ty); + bAttack.Hit(tx,ty); + bPause.Hit(tx,ty); + for(uint32_t i=0; ibuttons.size();i++){ + if(renderer->buttons[i]->active) + renderer->buttons[i]->Hit(tx,ty); + } + }else if(ta==IN_RELEASE){ + joyM.Release(tf); + joyL.Release(tf); + bAttack.pressed = false; + }else if(ta==IN_MOVE){ + joyM.Move(tx,ty,tf); + joyL.Move(tx,ty,tf); + joyL.vel.y*=-1; + }else{ + Log("touch %d %d\n",ta, tf); + } + } +#endif +} + +void tpsGame::OnMouseMove(float x, float y) +{ + mousePos = vec2(x,y); +} + +#ifndef ANDROID +#include +void tpsGame::OnKey(int key, int scancode, int action, int mods) +{ + if(gameState==eGamePlay){ + keyM.OnKey(key,action); + + if(action==GLFW_PRESS){ + if(key==GLFW_KEY_ESCAPE){ + PauseGame(); + } + if(key==GLFW_KEY_SPACE){ + bJump.pressed = true; + } + if(key==GLFW_KEY_E){ + bUse.pressed = true; + } + }else if(action==GLFW_RELEASE){ + } + } +} +#else +void tpsGame::OnKey(int key, int scancode, int action, int mods) +{ +} +#endif + diff --git a/demos/tpsGame/scene_txt_loader.cpp b/demos/tpsGame/scene_txt_loader.cpp new file mode 100644 index 0000000..72bdffd --- /dev/null +++ b/demos/tpsGame/scene_txt_loader.cpp @@ -0,0 +1,167 @@ + +#include +#include +#include +using namespace std; +#include +#include "log.h" +#include "system/FileSystem.h" +#include "scene/Scene.h" +#include "renderer/Model.h" +#include "resource/ResourceManager.h" +#include "tpsPhysics.h" +#include "tpsObjects.h" + +glm::mat4 PosRotToMtx(glm::vec3 pos, glm::vec3 rot){ + glm::mat4 mtx = glm::translate(glm::mat4(1),pos); + mtx = glm::rotate(mtx,glm::radians(rot.x),glm::vec3(1,0,0)); + mtx = glm::rotate(mtx,glm::radians(rot.y),glm::vec3(0,1,0)); + mtx = glm::rotate(mtx,glm::radians(rot.z),glm::vec3(0,0,1)); + return mtx; + + //vbsp + /*glm::mat4 m = glm::translate(glm::mat4(1.0), prop->m_Origin); + m = glm::rotate(m, glm::radians(prop->m_Angles.y), glm::vec3(0,0,1)); + m = glm::rotate(m, glm::radians(prop->m_Angles.x), glm::vec3(0,1,0)); + m = glm::rotate(m, glm::radians(prop->m_Angles.z), glm::vec3(1,0,0));*/ + //star-fleet + /* modelMtx = glm::translate(glm::mat4(1),position); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.y),glm::vec3(0,1,0)); + modelMtx = glm::rotate(modelMtx,glm::radians(angles.x),glm::vec3(1,0,0));*/ + //ME3 + /* modelMtx = glm::mat4(1.0); + modelMtx = glm::translate(modelMtx,location); + modelMtx = glm::rotate(modelMtx,rotation.y,glm::vec3(0,0,1)); + modelMtx = glm::rotate(modelMtx,rotation.x,glm::vec3(0,-1,0)); + modelMtx = glm::rotate(modelMtx,rotation.z,glm::vec3(-1,0,0)); + modelMtx = glm::scale(modelMtx,scale);*/ +} + +glm::vec3 ReadVec3(ifstream &file){ + glm::vec3 v; + file >> v.x; + file >> v.y; + file >> v.z; + return v; +} + +void LoadTXTSceneV1(ifstream &file, Scene *scene,ResourceManager *resMan,PhysicsSystem *physics); + +Scene *LoadTXTScene(const char *fileName,ResourceManager *resMan,PhysicsSystem *physics) +{ + string filePath = "scenes/"+string(fileName)+".txt"; + char path[256]; + g_fs.GetFilePath(filePath.c_str(), path); + ifstream file(path); + if(!file){ + Log("Scene file not found: %s\n", path); + return 0; + } + Log("Loading scene (%s)\n",fileName); + + Scene *scene = new Scene(); + + int ver = 0; + file >> ver; + + if(ver==1){ + LoadTXTSceneV1(file,scene,resMan,physics); + }else{ + Log("Unknown scene version %d (%s)\n",ver,fileName); + } + file.close(); + + return scene; +} + +void LoadTXTSceneV1(ifstream &file, Scene *scene,ResourceManager *resMan,PhysicsSystem *physics) +{ + scene->sunDirection = glm::normalize(vec3(0.3,0.4,0.7)); + + scene->startPos = ReadVec3(file); + scene->startRot = ReadVec3(file); + + int numObjects = 0; + file >> numObjects; + + char buff[1024]={0}; + glm::vec3 pos(0),rot(0); + for(int i=0;i> buff; + if(strcmp(buff,"StaticModel")==0){ + file >> buff; + Model *mdl = resMan->GetModel(buff); + if(!mdl) + Log("Error loading scene: Model not loaded (%s)\n",buff); + pos = ReadVec3(file); + rot = ReadVec3(file); + int collType = 0; + file >> collType; + + StaticModel *obj = new StaticModel(); + obj->mdl = mdl; + obj->modelMtx = PosRotToMtx(pos,rot); + + if(collType==1){//box + //boxSize + physics->AddBox(ReadVec3(file),obj); + } + + scene->AddObject(obj); + }else if(strcmp(buff,"Collider")==0){ + pos = ReadVec3(file); + rot = ReadVec3(file); + int collType = 0; + file >> collType; + + SceneObject *obj = new SceneObject(); + obj->modelMtx = PosRotToMtx(pos,rot); + + if(collType==1){//box + //boxSize + physics->AddBox(ReadVec3(file),obj); + } + scene->AddObject(obj); + }else if(strcmp(buff,"Button")==0){ + pos = ReadVec3(file); + ButtonObject *obj = new ButtonObject(pos); + file >> buff; + obj->name = buff; + file >> buff; + obj->text = buff; + file >> buff; + if(strcmp(buff,"tp")==0){ + obj->type = BUTTON_TP; + obj->targetPos = ReadVec3(file); + obj->targetRot = ReadVec3(file); + }else{ + obj->type = BUTTON_ACTIVE; + file >> buff; + obj->target = (ActivatingObject*)scene->Find(buff); + } + obj->mdl = g_mdlBox; + scene->AddObject(obj); + }else if(strcmp(buff,"Door")==0){ + pos = ReadVec3(file); + file >> buff; + DoorObject *obj = new DoorObject(pos,g_mdlBox,ReadVec3(file)); + obj->name = buff; + scene->AddObject(obj); + }else if(strcmp(buff,"Light")==0){ + int type = 0; + file >> type; + pos = ReadVec3(file); + vec3 col = ReadVec3(file); + float radius = 5; + file >> radius; + LightObject *l = new LightObject((eLightType)type,pos,col,radius); + scene->AddLight(l); + /*}else if(strcmp(buff,"Plane")==0){ + pos = ReadVec3(file); + rot = ReadVec3(file); + */ + }else{ + Log("Error loading scene: invalid object type (%s)\n",buff); + } + } +} diff --git a/demos/tpsGame/tpsMaterials.cpp b/demos/tpsGame/tpsMaterials.cpp new file mode 100644 index 0000000..ef99958 --- /dev/null +++ b/demos/tpsGame/tpsMaterials.cpp @@ -0,0 +1,51 @@ + + +#include +#include +#include +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/texture.h" +#include "resource/ResourceManager.h" +#include "tpsMaterials.h" + +using namespace std; + +IMaterial *TXTMaterialLoader::Load(const char *name){ + if(strstr(name,".vtf")){ + Texture *tex = resMan->GetTexture(name); + return new TexMaterial(tex,false); + }else{ + string filePath = "materials/"+string(name)+".txt"; + char path[256]; + g_fs.GetFilePath(filePath.c_str(), path); + ifstream mat_file(path); + if(!mat_file){ + Log("Material file %s not found\n", path); + return 0; + } + //shader + mat_file >> path; + bool lit = false; + if(!strcmp(path,"light")){ + lit = true; + } + + //texture + Texture *matTex; + mat_file >> path; + + matTex = resMan->GetTexture(path); + if(!matTex){ + Log("Unknown material texture: %s\n", path); + mat_file.close(); + return 0; + } + mat_file.close(); + Log("Load material %s: t %s, l %d\n",name,path,lit); + + return new TexMaterial(matTex,lit); + } + return 0; +} + diff --git a/demos/tpsGame/tpsMaterials.h b/demos/tpsGame/tpsMaterials.h new file mode 100644 index 0000000..18f848c --- /dev/null +++ b/demos/tpsGame/tpsMaterials.h @@ -0,0 +1,15 @@ + +#pragma once + +#include "renderer/Material.h" +class Texture; +class ResourceManager; + +class TXTMaterialLoader: public IMaterialLoader{ +public: + TXTMaterialLoader(ResourceManager *rm):IMaterialLoader(rm){} + virtual IMaterial *Load(const char *name); + virtual bool CheckExt(const char *name){return 1;/*strstr(name,".txt")!=0;*/} + virtual const char *GetExt(){return "txt";} +}; + diff --git a/demos/tpsGame/tpsMenu.cpp b/demos/tpsGame/tpsMenu.cpp new file mode 100644 index 0000000..4eb1d00 --- /dev/null +++ b/demos/tpsGame/tpsMenu.cpp @@ -0,0 +1,189 @@ + +#include +#include "tpsMenu.h" +#include "graphics/platform_gl.h" +#include "renderer/renderer.h" + +void GameLog(const char *s); +void tpsStartGame(); +void tpsResumeGame(); + +tpsSettings::tpsSettings(){ + tps = true; + drawTouch = true; + debugPhysics = false; + drawBbox = false; + screenScale = 1; +} + +void tpsSettings::Load(ConfigFile &cfg){ + tps = cfg.values["view"]=="tps"; + drawTouch = cfg.values["drawTouch"]!="0"; + debugPhysics = cfg.values["debugPhysics"]!="0"; + drawBbox = cfg.values["drawBbox"]!="0"; + screenScale = atof(cfg.values["screenScale"].c_str()); +} + + +void tpsMenu::Init(tpsSettings *s,IRenderer *r){ + sets = s; + tempScreenScale = sets->screenScale; + + bPlay = Button(0.1,0.5,0.6,0.08,"Play"); + bResume = Button(0.1,0.5,0.6,0.08,"Resume"); + bSettings = Button(0.1,0.6,0.6,0.08,"Settings"); + bQuit = Button(0.1,0.7,0.6,0.08,"Quit"); + bView = Button(0.04,0.6,0.5,0.08,"View:"); + bTouch = Button(0.04,0.5,0.5,0.08,"Draw touch:"); + bScreenScale = Button(0.04,0.4,0.5,0.08,"Screen scale"); + bBack = Button(0.04,0.7,0.5,0.08,"Back"); + + //r->AddButton(&bPlay); + buttons.push_back(&bPlay); + buttons.push_back(&bResume); + buttons.push_back(&bSettings); + buttons.push_back(&bQuit); + buttons.push_back(&bView); + buttons.push_back(&bTouch); + buttons.push_back(&bScreenScale); + buttons.push_back(&bBack); + + SetState(eMenuMain); +} + +void tpsMenu::Resize(float aspect){ + bPlay.w = 0.4/aspect; + bResume.w = 0.4/aspect; + bSettings.w = 0.4/aspect; + bQuit.w = 0.4/aspect; + bView.x = 0.04/aspect; + bView.w = 0.35/aspect; + bTouch.x = 0.04/aspect; + bTouch.w = 0.35/aspect; + bScreenScale.x = 0.04/aspect; + bScreenScale.w = 0.35/aspect; + bBack.x = 0.04/aspect; + bBack.w = 0.35/aspect; +} + +void tpsMenu::SetState(eMenuState s) +{ + prevState = state; + state = s; + + for(uint32_t i=0;iactive = false; + } + + switch(s){ + case eMenuMain: + bPlay.active = true; + case eMenuPause: + bSettings.active = true; + bQuit.active = true; + if(s==eMenuPause) + bResume.active = true; + break; + case eMenuSettings: + bTouch.active = true; + bView.active = true; + bScreenScale.active = true; + bBack.active = true; + break; + } +} + +void tpsMenu::Update(float deltaTime) +{ + if(bPlay.pressed){ + bPlay.pressed = false; + SetState(eMenuPause); + GameLog("play\n"); + //game->StartGame(); + tpsStartGame(); + } + if(bResume.pressed){ + bResume.pressed = false; + tpsResumeGame(); + } + if(bSettings.pressed){ + bSettings.pressed = false; + SetState(eMenuSettings); + } + if(bQuit.pressed){ + bQuit.pressed = false; + EngineQuit(); + } + + if(state==eMenuSettings){ + if(bBack.pressed){ + bBack.pressed = false; + SetState(prevState); + sets->screenScale = tempScreenScale; + } + if(bView.pressed){ + bView.pressed = false; + GameLog("change view\n"); + sets->tps=!sets->tps; + } + if(bTouch.pressed){ + bTouch.pressed = false; + sets->drawTouch = !sets->drawTouch; + } + if(bScreenScale.pressed){ + bScreenScale.pressed = false; + screenScaleState++; + screenScaleState%=32; + screenScaleState = screenScaleState%32; + tempScreenScale = ((32-screenScaleState)/32.0f); + } + } +} + +void tpsMenu::Draw(IRenderer *r) +{ + if(state==eMenuMain||state==eMenuPause){ + r->DrawText("Game title",0.04,0.25,1.5f*r->aspect); + }else if(state==eMenuSettings){ + r->DrawText("Settings",0.04,0.25,1.0f*r->aspect); + if(sets->tps) + r->DrawText("Third person",0.4,bView.y+0.05,0.4); + else + r->DrawText("First person",0.4,bView.y+0.05,0.4); + if(sets->drawTouch) + r->DrawText("On",0.4,bTouch.y+0.05,0.4); + else + r->DrawText("Off",0.4,bTouch.y+0.05,0.4); + + char str[64]; + snprintf(str,64,"%.3f",tempScreenScale); + r->DrawText(str,0.45,bScreenScale.y+0.05,0.4); + } + + glEnable(GL_BLEND); + for(uint32_t i=0;iactive){ + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + r->SetColor(1,1,1,0.2); + r->DrawRect(b->x,b->y,b->w,b->h); + r->SetColor(1,1,1,1); + if(b->text){ + glBlendFunc(1,1); + //r->DrawText(b->text,b->x*r->aspect+0.01,1-b->y-0.05,0.5); + r->DrawText(b->text,b->x*r->aspect+0.01,b->y+0.05,0.5); + } + } + } + glDisable(GL_BLEND); +} + +void tpsMenu::OnTouch(float x, float y, int a, int f){ + if(a==IN_PRESS){ + for(uint32_t i=0;iHit(x,y); + } + } +} + + diff --git a/demos/tpsGame/tpsMenu.h b/demos/tpsGame/tpsMenu.h new file mode 100644 index 0000000..cfff44a --- /dev/null +++ b/demos/tpsGame/tpsMenu.h @@ -0,0 +1,61 @@ + +#pragma once + +#include +#include "engine.h" +#include "button.h" +#include "system/config.h" + +class IRenderer; + +class tpsSettings{ +public: + tpsSettings(); + void Load(ConfigFile &cfg); + bool tps; + bool drawTouch; + bool debugPhysics; + bool drawBbox; + float screenScale; +}; + +enum eMenuState{ + eMenuMain = 1, + eMenuSettings = 2, + eMenuPause = 3 +}; +/* +class MenuButton: public Button{ +public: + MenuButton(float nx,float ny, float nw, float nh, const char *t){ + x=nx;y=ny;w=nw;h=nh; + text = t; + pressed = false; + } +}; +*/ +class tpsMenu{ +public: + void Init(tpsSettings *s,IRenderer *r); + void Resize(float aspect); + void SetState(eMenuState s); + void Update(float deltaTime); + void Draw(IRenderer *r); + void OnTouch(float x, float y, int a, int f); + + eMenuState state; + eMenuState prevState; + Button bPlay; + Button bResume; + Button bSettings; + Button bQuit; + Button bView; + Button bTouch; + Button bScreenScale; + Button bBack; + std::vector buttons; + tpsSettings *sets; + int screenScaleState; + float tempScreenScale; +}; + diff --git a/demos/tpsGame/tpsObjects.cpp b/demos/tpsGame/tpsObjects.cpp new file mode 100644 index 0000000..48e5244 --- /dev/null +++ b/demos/tpsGame/tpsObjects.cpp @@ -0,0 +1,107 @@ + +#include + +#include "renderer/Model.h" +#include "renderer/renderer.h" +#include "graphics/platform_gl.h" + +#include "tpsObjects.h" +#include "tpsPlayer.h" + +using glm::vec3; +using glm::mat4; +using glm::translate; +using glm::mix; +using glm::scale; +using glm::rotate; +using glm::radians; + +void GameLog(const char *s); +extern std::vector g_buttons; + +DoorObject::DoorObject(vec3 p,Model *m,vec3 s):pos(p),mdl(m){ + dynamic = true; + modelMtx = translate(mat4(1.0f),p); + state = DOOR_CLOSED; + closePos = p; + t = 0; + size = s; + openPos = p+vec3(0,s.y*2.0-0.05,0); +} +void DoorObject::Active(){ + GameLog("Door active\n"); + if(state&1)//moving + return; + if(state==DOOR_CLOSED) + state=DOOR_OPENING; + else if(state==DOOR_OPEN) + state = DOOR_CLOSING; +} +void DoorObject::Update(float deltaTime){ + if((state&1)==0) + return; + t+=deltaTime; + if(t>1){ + t=1; + } + if(state==DOOR_OPENING){ + pos=mix(closePos,openPos,t); + }else{ + pos=mix(openPos,closePos,t); + } + modelMtx = translate(mat4(1.0f),pos); + if(t==1){ + t = 0; + if(state==DOOR_OPENING) + state=DOOR_OPEN; + else + state=DOOR_CLOSED; + GameLog("door stop\n"); + } +} +void DoorObject::Draw(IRenderer *r){ + if(mdl){ + r->DrawModel(mdl,scale(modelMtx,size)); + } +} + +ButtonObject::ButtonObject(vec3 p):SceneObject(){ + dynamic = true; + modelMtx = translate(mat4(1.0f),p); + pos = p; + rot = 0; + target = 0; + type = BUTTON_NONE; + g_buttons.push_back(this); +} +ButtonObject::~ButtonObject(){ + for(std::vector::iterator it = g_buttons.begin();it!=g_buttons.end();it++){ + if(*it==this){ + g_buttons.erase(it); + break; + } + } +} +void ButtonObject::Update(){ + +} +void ButtonObject::Draw(IRenderer *r){ + r->SetModelMtx(rotate(scale(modelMtx,vec3(1,1,1)),radians(rot),vec3(0,1,0))); + //TODO fix (move to material?) + glDisable(GL_CULL_FACE); + r->DrawText(name.c_str(),-0.2,0.15,1); + glEnable(GL_CULL_FACE); + if(mdl){ + r->DrawModel(mdl,scale(modelMtx,vec3(0.1))); + } +} +void ButtonObject::Press(Player *pl){ + GameLog("button pressed\n"); + if(type==BUTTON_ACTIVE&&target) + target->Active(); + else if(type==BUTTON_TP){ + pl->SetPos(targetPos); + pl->SetRot(targetRot); + } +} + diff --git a/demos/tpsGame/tpsObjects.h b/demos/tpsGame/tpsObjects.h new file mode 100644 index 0000000..9faf313 --- /dev/null +++ b/demos/tpsGame/tpsObjects.h @@ -0,0 +1,57 @@ + +#pragma once + +#include +class Model; +class IRenderer; +class Player; + +extern Model *g_mdlBox; + +class ActivatingObject: public SceneObject{ +public: + virtual void Active()=0; +}; + +#define DOOR_CLOSED 0 +#define DOOR_CLOSING 1 +#define DOOR_OPEN 2 +#define DOOR_OPENING 3 + +class DoorObject: public ActivatingObject{ +public: + DoorObject(glm::vec3 p,Model *m,glm::vec3 s=glm::vec3(0.5,1,0.1)); + virtual void Active(); + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); + + glm::vec3 pos; + glm::vec3 openPos; + glm::vec3 closePos; + glm::vec3 size; + Model *mdl; + int state; + float t; +}; + +#define BUTTON_NONE 0 +#define BUTTON_ACTIVE 1 +#define BUTTON_TP 2 +class ButtonObject: public SceneObject{ +public: + ButtonObject(glm::vec3 p); + ~ButtonObject(); + virtual void Update(); + virtual void Draw(IRenderer *r); + virtual void Press(Player *pl); + + glm::vec3 pos; + float rot; + std::string text; + Model *mdl; + ActivatingObject *target; + glm::vec3 targetPos; + glm::vec3 targetRot; + int type; +}; + diff --git a/demos/tpsGame/tpsPhysics.cpp b/demos/tpsGame/tpsPhysics.cpp new file mode 100644 index 0000000..ce337ed --- /dev/null +++ b/demos/tpsGame/tpsPhysics.cpp @@ -0,0 +1,442 @@ + +#include "tpsPhysics.h" +#include +#include +#include +#include +#include "log.h" +#include "graphics/platform_gl.h" +#include "renderer/renderer.h" + +using glm::vec3; + +void neMotionState::getWorldTransform(btTransform ¢erOfMassWorldTrans) const{ + if(!obj) + return; + centerOfMassWorldTrans.setFromOpenGLMatrix((float*)&obj->modelMtx); +} + +void neMotionState::setWorldTransform(const btTransform ¢erOfMassWorldTrans){ + if(!obj) + return; + centerOfMassWorldTrans.getOpenGLMatrix((float*)&obj->modelMtx); +} + + +class neDebugDraw: public btIDebugDraw +{ + int mode; + IRenderer *renderer; + +public: + neDebugDraw(IRenderer *rend):mode(0),renderer(rend){} + void drawLine(const btVector3 &from, const btVector3 &to, const btVector3 &color){ + renderer->SetModelMtx(glm::mat4(1.0)); + + float verts[6] = {from.x(),from.y(),from.z(), to.x(), to.y(), to.z()}; + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, verts); + renderer->SetColor(color.x(),color.y(),color.z(),1); + glDrawArrays(GL_LINES,0,2); + } + void drawContactPoint(const btVector3 &PointOnB, const btVector3 &normalOnB, btScalar distance, int lifeTime, const btVector3 &color){ + } + void reportErrorWarning(const char *warningString){ + Log("Physics: %s\n",warningString); + } + void draw3dText(const btVector3 &location, const char *textString){ + //TODO + Log("Physics 3dtext: %s\n",textString); + } + void setDebugMode(int debugMode){ + mode = debugMode; + } + int getDebugMode() const{ + return mode; + } +}; + +void PhysicsSystem::Init(IRenderer *rend){ + collisionConfig = new btDefaultCollisionConfiguration(); + dispatcher = new btCollisionDispatcher(collisionConfig); + broadphase = new btDbvtBroadphase(); + solver = new btSequentialImpulseConstraintSolver(); + + world = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfig); + world->setGravity(btVector3(0, -9.8, 0)); + if(rend){ + world->setDebugDrawer(new neDebugDraw(rend)); + world->getDebugDrawer()->setDebugMode(1); + } + Log("PhysicsSystem Init\n"); +} + +void PhysicsSystem::Update(float deltaTime){ + world->stepSimulation(deltaTime); +} + +void PhysicsSystem::AddBox(vec3 size, SceneObject *so,float mass){ + btCollisionShape* shape = new btBoxShape(*((btVector3*)&size.x)); + shapes.push_back(shape); + + btVector3 inertia(0, 0, 0); + //if(mass>0) + shape->calculateLocalInertia(mass, inertia); + + btMotionState* motionState = new neMotionState(so); + btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(mass, motionState, shape, inertia); + btRigidBody *body = new btRigidBody(rigidBodyCI); + + world->addRigidBody(body); +} + +PlayerPhysics *PhysicsSystem::CreatePlayer(float height, float radius,glm::mat4 mtx){ + btCapsuleShape *shape = new btCapsuleShape(radius,height); + //btBoxShape *shape = new btBoxShape(btVector3(radius,height/2+radius,radius)); + shapes.push_back(shape); + + btTransform transform; + transform.setFromOpenGLMatrix((float*)&mtx); + + btPairCachingGhostObject *charGhostObj = new btPairCachingGhostObject(); + charGhostObj->setWorldTransform(transform); + charGhostObj->setCollisionShape(shape); + charGhostObj->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); + + PlayerPhysics *player = new PlayerPhysics(shape, charGhostObj, 50, mtx); + + broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback()); + world->addCollisionObject(charGhostObj); + world->addAction(player); + + return player; +} + +//=============================Player============================= + +vec3 PlayerPhysics::getPosition() const +{ + return *((vec3*)&curPos); +} + +void PlayerPhysics::updateAction(btCollisionWorld *collisionWorld, btScalar deltaTimeStep) +{ + preStep(collisionWorld); + playerStep(collisionWorld, deltaTimeStep); +} + +void PlayerPhysics::setWalkDirection(const btVector3 &walkDirection) +{ + walkDir = walkDirection; +} + +void PlayerPhysics::debugDraw(btIDebugDraw *debugDrawer) +{ + //Log("PlayerPhysics::debugDraw\n"); +} + +void PlayerPhysics::setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) +{ + Log("PlayerPhysics::setVelocityForTimeInterval\n"); +} + +void PlayerPhysics::reset(btCollisionWorld *collisionWorld) +{ + Log("PlayerPhysics::reset\n"); +} + +void PlayerPhysics::warp(const btVector3 &origin){ + btTransform xform; + xform.setIdentity(); + xform.setOrigin(origin); + ghostObj->setWorldTransform(xform); +} + +btVector3 computeReflectionDirection(const btVector3 & direction, const btVector3 & normal){ + return direction - (btScalar(2) * direction.dot(normal)) * normal; +} +btVector3 parallelComponent(const btVector3 & direction, const btVector3 & normal){ + btScalar magnitude = direction.dot(normal); + return normal * magnitude; +} +btVector3 perpindicularComponent(const btVector3 & direction, const btVector3 & normal){ + return direction - parallelComponent(direction, normal); +} + +void PlayerPhysics::stepUp(btCollisionWorld *collisionWorld){ + + targetPos = curPos + btVector3(0,stepHeight+(vertOffs > 0 ? vertOffs : 0),0); + + btTransform start, end; + start.setIdentity(); + end.setIdentity(); + start.setOrigin(curPos); + end.setOrigin(targetPos); + + ClosestNotMeConvexResultCallback callback(ghostObj,btVector3(0,1,0),0.4); + callback.m_collisionFilterGroup = ghostObj->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = ghostObj->getBroadphaseHandle()->m_collisionFilterMask; + + ghostObj->convexSweepTest(shape,start,end,callback,collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + + if(callback.hasHit()){ + Log("Player hit ceiling\n"); + if (callback.m_hitNormalWorld.dot(btVector3(0,1,0)) > 0){ + curPos.setInterpolate3(curPos, targetPos, callback.m_closestHitFraction); + curStepOffs = stepHeight * callback.m_closestHitFraction; + } + vertOffs = 0; + vertVel = 0; + }else{ + curPos = targetPos; + curStepOffs = stepHeight; + } + +} + +void PlayerPhysics::stepForward(btCollisionWorld *collisionWorld, btScalar dt){ + + targetPos = curPos + walkDir*dt; + + btTransform start, end; + start.setIdentity(); + end.setIdentity(); + + btScalar fraction = 1.0; + //btScalar distance2 = (curPos - targetPos).length2(); + + /*if (touchingContact){ + //broke stairs + // if (walkDir.normalize().dot(touchingNormal) > 0) + // updateTargetPositionBasedOnCollision(touchingNormal); + }*/ + + //int maxIter = 10; + //while (fraction > 0.01 && maxIter-- > 0) + { + start.setOrigin(curPos); + end.setOrigin(targetPos); + + ClosestNotMeConvexResultCallback callback(ghostObj,curPos-targetPos,0); + callback.m_collisionFilterGroup = ghostObj->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = ghostObj->getBroadphaseHandle()->m_collisionFilterMask; + + ghostObj->convexSweepTest(shape,start,end,callback,collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + + fraction -= callback.m_closestHitFraction; + + if(callback.hasHit()){ + updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld); +/* + btVector3 currentDir = targetPos - curPos; + distance2 = currentDir.length2(); + + if (distance2 > SIMD_EPSILON) + { + currentDir.normalize(); + + if (currentDir.dot(walkDir.normalize()) <= 0) + break; + } + else + break;*/ + }else{ + curPos = targetPos; + } + } +} + +void PlayerPhysics::stepDown(btCollisionWorld *collisionWorld, btScalar dt){ + + float downVel = (vertVel < 0 ? -vertVel : 0) * dt; + + if (downVel > 0 && downVel < stepHeight && (bOnGround || !jumping)) + downVel = stepHeight; + + targetPos -= btVector3(0,curStepOffs+downVel,0); + + btTransform start, end; + start.setIdentity(); + end.setIdentity(); + start.setOrigin(curPos); + end.setOrigin(targetPos); + + ClosestNotMeConvexResultCallback callback(ghostObj,btVector3(0,1,0),btCos(btRadians(85))); + callback.m_collisionFilterGroup = ghostObj->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = ghostObj->getBroadphaseHandle()->m_collisionFilterMask; + + ghostObj->convexSweepTest(shape,start,end,callback,collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + + if(callback.hasHit()){ + curPos.setInterpolate3(curPos, targetPos, callback.m_closestHitFraction); + vertVel = 0; + vertOffs = 0; + jumping = false; + }else{ + curPos = targetPos; + } +} + +void PlayerPhysics::preStep(btCollisionWorld *collisionWorld) +{ + int numPenetrationLoops = 0; + touchingContact = false; + + while(recoverFromPenetration(collisionWorld)) + { + numPenetrationLoops++; + touchingContact = true; + + if(numPenetrationLoops > 4) + break; + } + + curPos = ghostObj->getWorldTransform().getOrigin(); + targetPos = curPos; +} + +void PlayerPhysics::playerStep(btCollisionWorld *collisionWorld, btScalar dt) +{ + bOnGround = onGround(); + + //broken slower movement + //setRBForceImpulseBasedOnCollision(dt); + + vertVel -= 9.8*dt;//gravity + + //TODO clamp vertVel (jump, fall) + + vertOffs = vertVel*dt; + + stepUp(collisionWorld); + + stepForward(collisionWorld,dt); + + stepDown(collisionWorld,dt); + + btTransform transf = ghostObj->getWorldTransform(); + transf.setOrigin(curPos); + ghostObj->setWorldTransform(transf); + +} + +bool PlayerPhysics::canJump() const +{ + return onGround(); +} + +void PlayerPhysics::jump(const btVector3 &dir) +{ + if(!canJump()) + return; + vertVel = 5; + jumping = true; +} + +bool PlayerPhysics::onGround() const +{ + return vertVel == 0 && vertOffs == 0; +} + +void PlayerPhysics::setUpInterpolate(bool value) +{ + Log("PlayerPhysics::setUpInterpolate\n"); +} + +void PlayerPhysics::updateTargetPositionBasedOnCollision(const btVector3 & hitNormal, btScalar tangentMag, btScalar normalMag) +{ + btVector3 movementDirection = targetPos - curPos; + btScalar movementLenght = movementDirection.length(); + + if (movementLenght > SIMD_EPSILON) + { + movementDirection.normalize(); + + btVector3 reflectDir = computeReflectionDirection(movementDirection, hitNormal); + + btVector3 parallelDir, perpindicularDir; + + parallelDir = parallelComponent(reflectDir, hitNormal); + perpindicularDir = perpindicularComponent(reflectDir, hitNormal); + + targetPos = curPos; + + if (normalMag != 0) + { + btVector3 perpComponent = perpindicularDir * btScalar(normalMag * movementLenght); + targetPos += perpComponent; + } + } +} + +void PlayerPhysics::setRBForceImpulseBasedOnCollision(float dt) +{ + if (!walkDir.isZero()) + { + for (int i = 0; i < ghostObj->getOverlappingPairCache()->getNumOverlappingPairs(); i++) + { + btBroadphasePair * collisionPair = &ghostObj->getOverlappingPairCache()->getOverlappingPairArray()[i]; + + btRigidBody * rb = (btRigidBody*)collisionPair->m_pProxy1->m_clientObject; + + if (mass > rb->getInvMass()) + { + btScalar resultMass = mass - rb->getInvMass(); + btVector3 reflection = computeReflectionDirection(walkDir * resultMass * dt, walkDir.normalize()); + rb->applyCentralImpulse(reflection * -1); + } + } + + } +} + +bool PlayerPhysics::recoverFromPenetration(btCollisionWorld *collisionWorld) +{ + bool penetration = false; + + collisionWorld->getDispatcher()->dispatchAllCollisionPairs(ghostObj->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); + + curPos = ghostObj->getWorldTransform().getOrigin(); + + //btScalar maxPen = 0; + + btManifoldArray mManifoldArray; + for (int i = 0; i < ghostObj->getOverlappingPairCache()->getNumOverlappingPairs(); i++) + { + mManifoldArray.resize(0); + + btBroadphasePair * collisionPair = &ghostObj->getOverlappingPairCache()->getOverlappingPairArray()[i]; + + if (collisionPair->m_algorithm) + collisionPair->m_algorithm->getAllContactManifolds(mManifoldArray); + + for (int j = 0; j < mManifoldArray.size(); j++) + { + btPersistentManifold * manifold = mManifoldArray[j]; + btScalar directionSign = manifold->getBody0() == ghostObj ? btScalar(-1) : btScalar(1); + + for (int p = 0; p < manifold->getNumContacts(); p++) + { + const btManifoldPoint & pt = manifold->getContactPoint(p); + + btScalar dist = pt.getDistance(); + + if (dist < 0) + { + //maxPen = dist; + touchingNormal = pt.m_normalWorldOnB * directionSign; + penetration = true; + } + + curPos += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2); + + + } + } + } + + btTransform newTrans = ghostObj->getWorldTransform(); + newTrans.setOrigin(curPos); + ghostObj->setWorldTransform(newTrans); + + return penetration; +} diff --git a/demos/tpsGame/tpsPhysics.h b/demos/tpsGame/tpsPhysics.h new file mode 100644 index 0000000..50ccf44 --- /dev/null +++ b/demos/tpsGame/tpsPhysics.h @@ -0,0 +1,136 @@ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scene/Scene.h" + +class IRenderer; + +class neMotionState: public btMotionState{ +public: + SceneObject *obj; + + neMotionState():obj(0){ + } + neMotionState(SceneObject *so):obj(so){ + } + + virtual void getWorldTransform(btTransform& centerOfMassWorldTrans ) const; + virtual void setWorldTransform(const btTransform& centerOfMassWorldTrans); +}; + +class PlayerPhysics; + +class PhysicsSystem{ +public: + PhysicsSystem(){} + + void Init(IRenderer *rend); + void Update(float deltaTime); + void AddBox(glm::vec3 size, SceneObject *so,float mass=0); + btRigidBody *CreateRigidBody(float mass, btTransform tr, btCollisionShape *shape); + PlayerPhysics *CreatePlayer(float height,float radius, glm::mat4 mtx); + + btCollisionConfiguration *collisionConfig; + btDispatcher *dispatcher; + btBroadphaseInterface *broadphase; + btConstraintSolver *solver; + btDynamicsWorld *world; + + std::vector shapes; +}; + +class PlayerPhysics: public btCharacterControllerInterface{ +public: + PlayerPhysics(btConvexShape *shp, btPairCachingGhostObject *go, float aMass, glm::mat4 m): + shape(shp),ghostObj(go),mtx(m),stepHeight(0.5f),mass(aMass),walkDir(0,0,0),vertVel(0), + vertOffs(0),curStepOffs(0),bOnGround(false),jumping(false){ curPos=*((btVector3*)(&m[3])); } + + btConvexShape *shape; + btPairCachingGhostObject *ghostObj; + glm::mat4 mtx; + + glm::vec3 getPosition() const; + bool recoverFromPenetration(btCollisionWorld * collisionWorld); + void updateTargetPositionBasedOnCollision(const btVector3 & hitNormal, btScalar tangentMag = 0, btScalar normalMag = 1); + void setRBForceImpulseBasedOnCollision(float dt); + + void stepUp(btCollisionWorld *collisionWorld); + void stepForward(btCollisionWorld *collisionWorld, btScalar dt); + void stepDown(btCollisionWorld *collisionWorld, btScalar dt); + + void updateAction(btCollisionWorld *collisionWorld, btScalar deltaTimeStep); + void debugDraw(btIDebugDraw *debugDrawer); + void setWalkDirection(const btVector3 &walkDirection); + void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval); + void reset(btCollisionWorld *collisionWorld); + void warp(const btVector3 &origin); + void preStep(btCollisionWorld *collisionWorld); + void playerStep(btCollisionWorld *collisionWorld, btScalar dt); + bool canJump() const; + void jump(const btVector3 &dir); + bool onGround() const; + void setUpInterpolate(bool value); + + float stepHeight; + float mass; + btVector3 curPos; + btVector3 targetPos; + btVector3 walkDir; + bool touchingContact; + btVector3 touchingNormal; + float vertVel; + float vertOffs; + float curStepOffs; + bool bOnGround; + bool jumping; + + class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback + { + private: + btCollisionObject * mMe; + const btVector3 mUp; + btScalar mMinSlopeDot; + + public: + ClosestNotMeConvexResultCallback(btCollisionObject * me, const btVector3 & up, btScalar minSlopeDot) : + btCollisionWorld::ClosestConvexResultCallback(btVector3(0, 0, 0), btVector3(0, 0, 0)), + mMe(me), + mUp(up), + mMinSlopeDot(minSlopeDot) + {} + + btScalar addSingleResult(btCollisionWorld::LocalConvexResult & convexResult, bool normalInWorldSpace) + { + if (convexResult.m_hitCollisionObject == mMe) + return 1.0; + + btVector3 hitNormalWorld; + + if (normalInWorldSpace) + hitNormalWorld = convexResult.m_hitNormalLocal; + else + hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal; + + btScalar dotUp = mUp.dot(hitNormalWorld); + + if (dotUp < mMinSlopeDot) + return 1.0; + + return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + } + }; +}; + diff --git a/demos/tpsGame/tpsPlayer.cpp b/demos/tpsGame/tpsPlayer.cpp new file mode 100644 index 0000000..01c3f4c --- /dev/null +++ b/demos/tpsGame/tpsPlayer.cpp @@ -0,0 +1,123 @@ + +#include "tpsPlayer.h" +#include "renderer/camera.h" +#include "renderer/Model.h" +#include "renderer/renderer.h" +#include "button.h" + +#include + +using glm::vec3; +using glm::vec4; +using glm::mat4; +using glm::translate; + +void GameLog(const char *s); + +Player::Player(Camera *c,glm::vec3 start,glm::vec3 startRot,Model *m):pos(start),rot(startRot),cam(c),mdl(m),move(0),look(0),jump(0),bAttack(0),tps(true),weapon(0),phys(0){ + dynamic = true; + modelMtx = glm::translate(glm::mat4(1),pos); + modelMtx = glm::rotate(modelMtx,glm::radians(rot.y),glm::vec3(0,1,0)); +} + +void Player::Update(float deltaTime) +{ + float cameraHeight = 1.6; + //cameraHeight = 1; + float moveSpeed = 4; + + + pos = phys->getPosition()+vec3(0,-0.9,0); + if(jump->pressed){ + jump->pressed=false; + phys->jump(btVector3(0,1,0)); + //GameLog("jump\n"); + } + +#ifdef ANDROID + rot.y+=-glm::clamp(look->vel.x,-0.4f,0.4f)*270.0f*deltaTime; + if(glm::abs(rot.y)>180.0f){ + rot.y = rot.y-(int(rot.y)/360)*360.0f; + } + rot.x+=look->vel.y*40.0f*deltaTime; +#else + rot.y += -look->vel.x*270.0f; + rot.x += look->vel.y*160.0f; +#endif + rot.x = glm::clamp(rot.x,-85.0f,85.0f); + + glm::mat4 rotMtx(1.0); + rotMtx = glm::rotate(rotMtx,glm::radians(rot.y),glm::vec3(0,1,0)); + + glm::vec3 vel(move->vel.x,0.0f,-move->vel.y); + if(glm::length(vel)>0.01f){ + vel*=8.0;//joystick sense + if(glm::length(vel)>moveSpeed){ + vel=glm::normalize(vel)*moveSpeed; + } + vel = glm::mat3(rotMtx)*vel; + //noclip + //pos += vel*deltaTime; + phys->setWalkDirection(*((btVector3*)&vel)); + + if(glm::abs(rot.y-rot.z)>deltaTime*500.0f){ + rot.z += glm::sign(rot.y-rot.z)*500.0f*deltaTime; + }else{ + rot.z = rot.y; + } + }else{ + phys->setWalkDirection(btVector3(0,0,0)); + } + + if(!tps){ + rot.z = rot.y; + } + + rotMtx = glm::mat4(1.0); + rotMtx = glm::translate(rotMtx,glm::vec3(0,cameraHeight,0)); + rotMtx = glm::rotate(rotMtx,glm::radians(rot.y),glm::vec3(0,1,0)); + rotMtx = glm::rotate(rotMtx,glm::radians(-rot.x),glm::vec3(1,0,0)); + + view = glm::mat3(rotMtx)*glm::vec3(0,0,-1); + + if(weapon&&bAttack->pressed){ + vec3 weapPos = pos+vec3(rotMtx*vec4(0.1f,-0.1f,0.0f,1.0f)); + weapon->Attack(weapPos,view); + if(!weapon->autoFire) + bAttack->pressed = false; + } + + vec3 camOffs; + if(tps) + camOffs = glm::vec3(rotMtx*glm::vec4(0.55,0,1.5,1)); + else + camOffs = vec3(0,1.7,0); + + cam->rot = glm::vec3(rot.x,rot.y,0); + cam->pos = pos+camOffs; + cam->UpdateView(); + modelMtx = glm::translate(glm::mat4(1),pos); + modelMtx = glm::rotate(modelMtx,glm::radians(rot.z),glm::vec3(0,1,0)); +} + +void Player::Draw(IRenderer *r){ + if(tps) + r->DrawModel(mdl, modelMtx); + if(weapon&&weapon->mdl){ + mat4 mtx = modelMtx; + mtx = glm::translate(mtx,glm::vec3(0,1.65,-0.05)); + mtx = glm::rotate(mtx,glm::radians(-rot.x),glm::vec3(1,0,0)); + mtx = mtx*weapon->offsetMtx; + r->DrawModel(weapon->mdl,mtx); + } +} + +void Player::SetPos(vec3 p){ + pos = p; + phys->warp(*((btVector3*)&p)); +} + +void Player::SetRot(vec3 r){ + rot=vec3(r.x,r.y,r.y); +} + diff --git a/demos/tpsGame/tpsPlayer.h b/demos/tpsGame/tpsPlayer.h new file mode 100644 index 0000000..fd1dd7f --- /dev/null +++ b/demos/tpsGame/tpsPlayer.h @@ -0,0 +1,38 @@ + +#pragma once + +#include +#include +#include "scene/Scene.h" +class Camera; +class Model; +class Button; +class Joystick; +class IRenderer; + +#include "tpsPhysics.h" +#include "tpsWeapon.h" + +class Player: public SceneObject{ +public: + Player(Camera *c,glm::vec3 start,glm::vec3 startRot,Model *m); + + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); + void SetPos(glm::vec3 p); + void SetRot(glm::vec3 r); + + glm::vec3 pos; + glm::vec3 rot;//x,camY,charY + glm::vec3 view; + Camera *cam; + Model *mdl; + Joystick *move; + Joystick *look; + Button *jump; + Button *bAttack; + bool tps; + Weapon *weapon; + + PlayerPhysics *phys; +}; diff --git a/demos/tpsGame/tpsWeapon.cpp b/demos/tpsGame/tpsWeapon.cpp new file mode 100644 index 0000000..6a7ac7d --- /dev/null +++ b/demos/tpsGame/tpsWeapon.cpp @@ -0,0 +1,42 @@ + +#include "tpsWeapon.h" +#include "renderer/Model.h" +#include "renderer/renderer.h" +#include +using glm::translate; +using glm::scale; + +Weapon::Weapon(): mdl(0),offsetMtx(1.0f),damage(0),rate(1),type(eWeapNone){ + +} + +Weapon::Weapon(eWeaponType tp, Model *m, float dmg, float rt):mdl(m),offsetMtx(1.0f),damage(dmg),rate(rt),type(tp){ + +} + +bool Weapon::Attack(vec3 pos, vec3 vel){ + ShootBullet(pos,vel*50.0f); + return true; +} + +Projectile::Projectile(vec3 p, vec3 v):pos(p),vel(v),t(0){ + dynamic = true; +} + +void Projectile::Update(float deltaTime){ + t += deltaTime; + if(t>5) + return; + pos += vel*deltaTime; + modelMtx = glm::translate(glm::mat4(1),pos); + modelMtx = glm::scale(modelMtx,vec3(0.02f)); +} + +extern Model *g_mdlBox; + +void Projectile::Draw(IRenderer *r){ + if(t>5) + return; + r->DrawModel(g_mdlBox, modelMtx); +} + diff --git a/demos/tpsGame/tpsWeapon.h b/demos/tpsGame/tpsWeapon.h new file mode 100644 index 0000000..03748fa --- /dev/null +++ b/demos/tpsGame/tpsWeapon.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "scene/Scene.h" +#include +#include +using glm::vec3; + +class IRenderer; +class Model; + +void ShootBullet(vec3 orig, vec3 dir); + +enum eWeaponType{ + eWeapNone, + eWeapMellee, + eWeapPistol +}; + +class Weapon{ +public: + Weapon(); + Weapon(eWeaponType tp, Model *m, float dmg, float rt); + + bool Attack(vec3 pos, vec3 vel); + + Model *mdl; + glm::mat4 offsetMtx; + float damage; + float rate; + eWeaponType type; + bool autoFire; +}; + +class Projectile: public SceneObject{ +public: + Projectile(vec3 p, vec3 v); + virtual void Update(float deltaTime); + virtual void Draw(IRenderer *r); + + vec3 pos; + vec3 vel; + float t; +}; + diff --git a/docs/TODO.txt b/docs/TODO.txt new file mode 100644 index 0000000..b61fde3 --- /dev/null +++ b/docs/TODO.txt @@ -0,0 +1,75 @@ +see graphics/TODO.txt + +GL extensions checker + +Multithreading + +Menu * +Text enter +Console +Config * +MeshGenerator * +vbsp loader to separate class +mdl animation and skinning +UI toolkit * +Mono text rendering +Stencil fog + +Modular weapon system +Multiplayer * +ETC1 textures * +Complete DDS * +Engine resource system +Scene class * +Model format +PCC loader + +Skining with float textures +Native egl init +Refactor +VertexArrayObject +ErrorMessage window +rewrite Buttons +GetFileList +Remove glew +Network clients list +3D sound + +Done: +engine: + fs + log + time + touch input + keyboard input + mouse input +graphics: + GL extensions + glsl shaders + vbo + fbo + texture + png (optional) + vtf + dds (simple) + software dxt decompression + cubemaps + mesh files + .mesh (simple) + .obj (shit) + need new format + font + camera +cull: + bounding box + ray intersect + frustum + Contains aabb + Contains obb + Contains sphere +network: + manager + ip address class + udp socket +sound: + OpenAL + OpenSLES + mp3 music + wav sounds +bullet physics (optional) diff --git a/docs/engine_structure.txt b/docs/engine_structure.txt new file mode 100644 index 0000000..04d1248 --- /dev/null +++ b/docs/engine_structure.txt @@ -0,0 +1,24 @@ + +android_backend + log + IGame + CreateGame() + FileSystem +renderer + vbo + glslProg + texture + mesh_loader + mesh + font + ResourceManager + dds_loader + vtf_loader + nmf_loader + Model + BoundingBox + GetLighting() + fbo + gl_utils + scene + diff --git a/docs/model_format.txt b/docs/model_format.txt new file mode 100644 index 0000000..11e3229 --- /dev/null +++ b/docs/model_format.txt @@ -0,0 +1,44 @@ +Nenuzhno model format *.nmf + +v1.0 + +Header: +magic (NMF\0) 4b +version 4b (2b.2b) +length 4b +hash (?) + +vertsCount +vertsOffs +indSize +indsCount (if 0, not indexed) +indsOffs + +vertexFormat (bit mask pos,nrm,uv,uv2,tangents,weights, etc.) +stride + +SubMeshes (offset and length in inds or verts) +Mateials names + +v1.1 +collidersCount +collidersOffset +BoundingBox + +todo: +v2.0 +vertexFormat: +numAttribs +attrib{ +name? or enum? +size, type, normalize, stride, offset +} +can be in one buffer (abcabc) or in few (aabbcc) + + +BoundingBox +Bones +Sequences (+external) +Attachments (to bones) +Physics (collision) + diff --git a/docs/nenuzhno-engine.txt b/docs/nenuzhno-engine.txt new file mode 100644 index 0000000..30f0853 --- /dev/null +++ b/docs/nenuzhno-engine.txt @@ -0,0 +1,51 @@ +nenuzhno-engine documentation +v 0.1 + +Игровой движок "ненужно" +Ðвтор: ÐлекÑей Иванчуков lewa_j + +Обзор. +Язык C++ +ГрафичеÑкий API OpenGL 3.x/OpenGLES 2.0 +Физика Bullet +Звук OpenAL +Сеть UDP sockets +Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ Ð¾ÐºÐ¾Ð½Ð½Ð¾Ð¹ ÑиÑтемой иÑпользуетÑÑ glfw. +Ð”Ð»Ñ 3D математики иÑпользуетÑÑ glm. + +Графика. +Движок предоÑтавлÑет интерфейÑÑ‹ Ð´Ð»Ñ Ñледующих графичеÑких объектов: +Texture +ArrayBeffer (VertexBufferObject, IndexBufferObject) +FrameBufferObject +Ð¨ÐµÐ¹Ð´ÐµÑ€Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° +VertexArrayObject + +И Ð´Ð»Ñ Ñледующих абÑтракций над ними: +Font +Mesh +Model +Material +Renderer +Camera +Scene + +РеÑурÑÑ‹. +ПоддерживаютÑÑ Ñледующие форматы: +ТекÑтур: dds, vtf +Моделей: nmf, mesh, obj +(nmf - Nenuzhno Model Format) + +ResourceManager предоÑтавлÑет доÑтуп к загрузке реÑурÑов и позволÑет добавлÑÑ‚ÑŒ Ñвои загрузчики. +Методы: +bool Init()`- Ð˜Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ +Получение реÑурÑов по имени: +Model *GetModel(const char *name) +IMaterial *GetMaterial(const char *name) +Texture *GetTexture(const char *name) +Добавление Ñвоих загрузчиков: +void AddModelLoader(IModelLoader *loader) +void AddMaterialLoader(IMaterialLoader *loader) +void AddTextureLoader(ITextureLoader *loader) + + diff --git a/src/Android.mk b/src/Android.mk new file mode 100644 index 0000000..25c660a --- /dev/null +++ b/src/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) +ENGINE_DIR = ../../nenuzhno-engine/ +include $(CLEAR_VARS) + +LOCAL_MODULE := gles-test +LOCAL_CFLAGS := -Wall +LOCAL_C_INCLUDES := ../libs/glm ../nenuzhno-engine +LOCAL_SRC_FILES := $(ENGINE_DIR)android_backend.cpp $(ENGINE_DIR)file_system.cpp $(ENGINE_DIR)renderer/glslProg.cpp $(ENGINE_DIR)triangle.cpp +LOCAL_LDLIBS := -llog -lGLESv2 -lm -lOpenSLES +LOCAL_STATIC_LIBRARIES = + +include $(BUILD_SHARED_LIBRARY) + +$(call import-add-path,/sdcard/AppProjects/libs) diff --git a/src/Makefile.linux b/src/Makefile.linux new file mode 100644 index 0000000..d443e58 --- /dev/null +++ b/src/Makefile.linux @@ -0,0 +1,32 @@ +TARGET = libnenuzhno-engine.a + +all: $(TARGET) + +INCLUDES = -I. -I../../../Libs/gl/glew-2.1.0/include -I../../../Libs/gl/glfw-3.2.1_src/include -I../../../Libs/gl/glm/glm +CPPFLAGS = -Wall -ggdb +LDFLAGS = -static-libgcc -static-libstdc++ +LIBS = -L../../../Libs/gl/glfw-3.2.1_src/lib -lglfw3 -lX11 -lXrandr -lXinerama -lXcursor -lpthread -ldl -L../../../Libs/gl/glew-2.1.0/lib -lGLEW -lGL +SRCS = main.cpp log.cpp system/config.cpp system/FileSystem.cpp game/IGame.cpp network/network.cpp network/udp_socket.cpp \ + graphics/gl_utils.cpp graphics/gl_ext.cpp graphics/glsl_prog.cpp graphics/bin_gpu_prog.cpp graphics/vbo.cpp graphics/vao.cpp graphics/fbo.cpp graphics/texture.cpp \ + renderer/renderer.cpp renderer/LightingForward.cpp renderer/mesh.cpp renderer/Model.cpp renderer/font.cpp renderer/camera.cpp \ + scene/Scene.cpp cull/frustum.cpp cull/BoundingBox.cpp \ + resource/ResourceManager.cpp resource/nmf_loader.cpp resource/mesh_loader.cpp resource/obj_loader.cpp resource/vtf_loader.cpp resource/dds_loader.cpp \ + renderer/BokehBlur.cpp + +SRC_SOUND = sound/sound_al.cpp sound/AudioClip.cpp + +OBJS = $(patsubst %.cpp,obj_lin/%.o,$(SRCS)) + +obj_lin/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +nenuzhno-engine: $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) + +libnenuzhno-engine.a: $(OBJS) + $(AR) rcs $@ $^ + + +renderer/Model.cpp: renderer/Model.h +resource/nmf_loader.cpp: renderer/Model.h + diff --git a/src/Makefile.mingw b/src/Makefile.mingw new file mode 100644 index 0000000..7ca5cb7 --- /dev/null +++ b/src/Makefile.mingw @@ -0,0 +1,44 @@ +#TARGET = nenuzhno-engine.exe +TARGET = ../bin/libnenuzhno-engine.a +LIBS_DIR = ../../../../Libs + +all: $(TARGET) + +INCLUDES = -I. -I$(LIBS_DIR)/gl/glew-2.0.0/include -I$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/include -I$(LIBS_DIR)/gl/glm/glm -I$(LIBS_DIR)/OpenAL-soft/include +CPPFLAGS = -Wall -ggdb +LDFLAGS = -static-libgcc -static-libstdc++ +LIBS = -lglfw3 -lgdi32 -lglew32 -lopengl32 -L$(LIBS_DIR)/gl/glfw-3.3.bin.WIN32/lib-mingw-w64 -L$(LIBS_DIR)/gl/glew-2.0.0/lib -lOpenAL32 -L$(LIBS_DIR)/OpenAL-soft/libs/Win32 +SRCS = main.cpp log.cpp system/config.cpp system/FileSystem.cpp game/IGame.cpp sound/sound_al.cpp sound/AudioClip.cpp network/network.cpp network/udp_socket.cpp \ + graphics/gl_utils.cpp graphics/gl_ext.cpp graphics/glsl_prog.cpp graphics/bin_gpu_prog.cpp graphics/vbo.cpp graphics/vao.cpp graphics/fbo.cpp graphics/texture.cpp \ + renderer/renderer.cpp renderer/LightingForward.cpp renderer/mesh.cpp renderer/Model.cpp renderer/font.cpp renderer/camera.cpp \ + scene/Scene.cpp cull/frustum.cpp cull/BoundingBox.cpp \ + resource/ResourceManager.cpp resource/nmf_loader.cpp resource/mesh_loader.cpp resource/obj_loader.cpp resource/vtf_loader.cpp resource/dds_loader.cpp \ + renderer/BokehBlur.cpp + +OBJS = $(patsubst %.cpp,obj/%.o,$(SRCS)) + +obj/%.o: %.cpp + $(CXX) -c $< -o $@ $(CPPFLAGS) $(INCLUDES) + +%.exe: $(OBJS) obj + $(CXX) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) + +../bin/libnenuzhno-engine.a: $(OBJS) + $(AR) rcs $@ $^ + +obj: + mkdir obj + mkdir obj\sound + mkdir obj\renderer + mkdir obj\cull + mkdir obj\network + +.PHONY: clean + +clean: + del $(subst /,\,$(OBJS)) + +#rebuild: clean all + +renderer/Model.cpp: renderer/model.h +resource/nmf_loader.cpp: renderer/Model.h diff --git a/src/android_backend.cpp b/src/android_backend.cpp new file mode 100644 index 0000000..620bf68 --- /dev/null +++ b/src/android_backend.cpp @@ -0,0 +1,88 @@ + +#include + +void Created(); +void Changed(int w, int h); +void Draw(); +void OnTouch(float tx, float ty, int ta, int tf); +void OnKey(int key, int scancode, int action, int mods); + +#include "log.h" +#include "game/IGame.h" +#include "system/FileSystem.h" + +#include "stdlib.h" +void EngineError(const char *message) +{ + LOG("EngineError!!! (%s)\n", message); + exit(-1); +} + +void EngineQuit(){ + Log("EngineQuit()\n"); + exit(0); +} + +#include +void EngineSwapBuffers() +{ + eglSwapBuffers(eglGetCurrentDisplay(),eglGetCurrentSurface(EGL_DRAW)); +} + +#include +double GetTime() +{ + timeval tp; + //timezone tzp; + static int secbase; + + //gettimeofday(&tp, &tzp); + gettimeofday(&tp, 0); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +void EnableCursor(bool s) +{ + //nothing +} + +//#define JNI_PACK Java_ru_lewa_1j_ndkglestest1_GLJNILib_ +#define JNI_PACK(s) Java_ru_lewa_1j_nenuzhno_engine_JNILib_ ## s + +extern "C" +{ + JNIEXPORT void JNICALL JNI_PACK(created)(JNIEnv * env, jclass cls) + { + GameInit(); + g_fs.Init(pGame->GetGamedir()); + LogInit(); + Created(); + } + + JNIEXPORT void JNICALL JNI_PACK(changed)(JNIEnv * env, jclass cls, jint width, jint height) + { + Changed(width, height); + } + + JNIEXPORT void JNICALL JNI_PACK(draw)(JNIEnv * env, jclass cls) + { + Draw(); + } + + JNIEXPORT void JNICALL JNI_PACK(ontouch)(JNIEnv * env, jclass cls, jfloat tx, jfloat ty, jint ta, jint tf) + { + OnTouch(tx, ty, ta, tf); + } + + JNIEXPORT void JNICALL JNI_PACK(onkey)(JNIEnv * env, jclass cls, jint k, jint a) + { + OnKey(k, 0, a, 0); + } +} diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..898886c --- /dev/null +++ b/src/button.h @@ -0,0 +1,146 @@ + +#pragma once + +#include "input.h" +#include + +class Button +{ +public: + float x,y, w,h; + int type; + const char *text; + bool active; + bool pressed; + void (*func)(); + + Button():x(0),y(0),w(0),h(0),type(0),text(0),active(0),pressed(0){func=0;} + Button(float nx, float ny, float nw, float nh, bool adjust = false); + Button(float nx, float ny, float nw, float nh, const char *t, bool adjust=false); + + bool SetUniform(int loc); + virtual bool Hit(float tx, float ty){ + if(!active) + return false; + if(tx>x+w||txy+h||ty +#include "BoundingBox.h" +#include "renderer/Model.h" +#include "engine.h" + +using glm::vec3; + +bool BoundingBox::Intersect(vec3 orig, vec3 dir){ + vec3 tMin = (min - orig) / dir; + vec3 tMax = (max - orig) / dir; + vec3 t1 = glm::min(tMin, tMax); + vec3 t2 = glm::max(tMin, tMax); + float tNear = glm::max(glm::max(t1.x, t1.y), t1.z); + float tFar = glm::min(glm::min(t2.x, t2.y), t2.z); + //return vec2(tNear, tFar); + return (tNear > 0.0 && tNear < 1.0 && tNear < tFar); +} + +bool BoundingBox::Contains(glm::vec3 pos, float radius){ + //for(int i=0; i<6; i++){ + //if(glm::dot(glm::vec3(planes[i]),pos) + planes[i].w <= -radius) + // return false; + //} + return true; +} + +void BoundingBox::FromVerts(const char *verts, int num, const vertAttrib_t &va){ + if(va.size!=3||va.type!=GL_FLOAT){ + EngineError("BoundingBox::FromVerts(): vertex type not a vec3"); + return; + } + + min = vec3(999999); + max = vec3(-999999); + + for(int i=0;i + +struct vertAttrib_t; + +class BoundingBox +{ +public: + glm::vec3 min; + glm::vec3 max; + + BoundingBox() + {} + BoundingBox(glm::vec3 mn, glm::vec3 mx): min(mn),max(mx) + {} + BoundingBox(short mn[3], short mx[3]) + { + min = glm::vec3(mn[0],mn[1],mn[2]); + max = glm::vec3(mx[0],mx[1],mx[2]); + } + + bool Intersect(glm::vec3 orig, glm::vec3 dir); + bool Contains(glm::vec3 pos, float radius); + + void FromVerts(const char *verts,int num, const vertAttrib_t &va); + //void AddVert(glm::vec3 v) + //{} +}; + diff --git a/src/cull/frustum.cpp b/src/cull/frustum.cpp new file mode 100644 index 0000000..6e18036 --- /dev/null +++ b/src/cull/frustum.cpp @@ -0,0 +1,114 @@ + +#include "cull/frustum.h" +#include + +void Frustum::Init(glm::mat4 mtx) +{ + mtx = glm::transpose(mtx); + planes[0] = mtx[3]+mtx[0]; + planes[1] = mtx[3]-mtx[0]; + planes[2] = mtx[3]+mtx[1]; + planes[3] = mtx[3]-mtx[1]; + planes[4] = mtx[3]+mtx[2]; + planes[5] = mtx[3]-mtx[2]; + + + for(int i=0; i<6; i++){ + float l = glm::length(glm::vec3(planes[i])); + planes[i] /= l; + } +} + +bool Frustum::Contains(glm::vec3 min, glm::vec3 max) +{ + for(int i=0; i<6; i++){ + if(glm::dot(glm::vec3(planes[i]),min) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(min.x,min.y,max.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(min.x,max.y,min.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(min.x,max.y,max.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(max.x,min.y,min.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(max.x,min.y,max.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(max.x,max.y,min.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),max) + planes[i].w > 0) + continue; + return false; + } + return true; +} + +bool Frustum::Contains(BoundingBox bb) +{ + for(int i=0; i<6; i++) + { + if(glm::dot(glm::vec3(planes[i]),bb.min) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(bb.min.x,bb.min.y,bb.max.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(bb.min.x,bb.max.y,bb.min.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(bb.min.x,bb.max.y,bb.max.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(bb.max.x,bb.min.y,bb.min.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(bb.max.x,bb.min.y,bb.max.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),glm::vec3(bb.max.x,bb.max.y,bb.min.z)) + planes[i].w > 0) + continue; + if(glm::dot(glm::vec3(planes[i]),bb.max) + planes[i].w > 0) + continue; + return false; + } + return true; +} + +bool Frustum::Contains(BoundingBox bb, glm::mat4 mtx) +{ + glm::vec3 vecs[8]={ + bb.min, + glm::vec3(bb.min.x,bb.min.y,bb.max.z), + glm::vec3(bb.min.x,bb.max.y,bb.min.z), + glm::vec3(bb.min.x,bb.max.y,bb.max.z), + glm::vec3(bb.max.x,bb.min.y,bb.min.z), + glm::vec3(bb.max.x,bb.min.y,bb.max.z), + glm::vec3(bb.max.x,bb.max.y,bb.min.z), + bb.max + }; + for(int v=0;v<8;v++) + { + vecs[v] = glm::vec3(mtx*glm::vec4(vecs[v],1.0)); + } + for(int i=0; i<6; i++) + { + int in = 8; + for(int v=0;v<8;v++) + { + if(glm::dot(glm::vec3(planes[i]),vecs[v]) + planes[i].w < 0) + in--; + } + if(in==0) + return false; + } + return true; +} + +bool Frustum::Contains(glm::vec4 sphere) +{ + for(int i=0; i<6; i++){ + /*float dist = glm::dot(glm::vec3(sphere),glm::vec3(planes[i]))+planes[i].w; + if(dist < -sphere.w) + return false; + if(glm::abs(dist) < sphere.w) + return true; + */ + if(glm::dot(glm::vec3(planes[i]),glm::vec3(sphere)) + planes[i].w <= -sphere.w) + return false; + } + return true; +} diff --git a/src/cull/frustum.h b/src/cull/frustum.h new file mode 100644 index 0000000..4d16d83 --- /dev/null +++ b/src/cull/frustum.h @@ -0,0 +1,23 @@ + +#pragma once + +#include +#include +#include +#include "cull/BoundingBox.h" + +class Frustum +{ +public: + glm::vec4 planes[6]; + + Frustum() + {} + + void Init(glm::mat4 mtx); + bool Contains(glm::vec3 min, glm::vec3 max); + bool Contains(BoundingBox bb); + bool Contains(BoundingBox bb, glm::mat4 mtx); + bool Contains(glm::vec4 sphere); +}; + diff --git a/src/engine.h b/src/engine.h new file mode 100644 index 0000000..a3cafa2 --- /dev/null +++ b/src/engine.h @@ -0,0 +1,12 @@ + +#pragma once + +void EngineError(const char *message); +void EngineQuit(); + +void EngineSwapBuffers(); + +double GetTime(); + +void EnableCursor(bool state); + diff --git a/src/game/IGame.cpp b/src/game/IGame.cpp new file mode 100644 index 0000000..74ae9a5 --- /dev/null +++ b/src/game/IGame.cpp @@ -0,0 +1,38 @@ + +#include "game/IGame.h" +#include "log.h" + +IGame *pGame = 0; + +void GameInit(){ + Log("GameInit()\n"); + pGame = CreateGame(); +} + +void Created(){ + pGame->Created(); +} + +void Changed(int w, int h){ + pGame->Changed(w, h); +} + +void Draw(){ + pGame->Draw(); +} + +void OnTouch(float x, float y, int a, int tf){ + pGame->OnTouch(x, y, a, tf); +} + +void OnMouseMove(float x, float y){ + pGame->OnMouseMove(x, y); +} + +void OnScroll(float sx, float sy){ + pGame->OnScroll(sx, sy); +} + +void OnKey(int key, int scancode, int action, int mods){ + pGame->OnKey(key, scancode, action, mods); +} diff --git a/src/game/IGame.h b/src/game/IGame.h new file mode 100644 index 0000000..10323b8 --- /dev/null +++ b/src/game/IGame.h @@ -0,0 +1,22 @@ + +#pragma once + +void GameInit(); + +class IGame{ +public: + virtual void Created()=0; + virtual void Changed(int w, int h)=0; + virtual void Draw()=0; + virtual const char* GetGamedir()=0; + + virtual void OnKey(int key, int scancode, int action, int mods){} + virtual void OnTouch(float tx, float ty, int ta, int tf){} + virtual void OnMouseMove(float x, float y){} + virtual void OnScroll(float sx, float sy){} +}; + +extern IGame *pGame; + +//implemented in game code +IGame *CreateGame(); diff --git a/src/graphics/ArrayBuffer.h b/src/graphics/ArrayBuffer.h new file mode 100644 index 0000000..b40f32e --- /dev/null +++ b/src/graphics/ArrayBuffer.h @@ -0,0 +1,31 @@ + +#pragma once + +#include "graphics/platform_gl.h" + +class ArrayBuffer{ +public: + GLuint id; + GLenum target; + + ArrayBuffer(); + virtual ~ArrayBuffer(); + + virtual void Create(); + virtual void Bind(); + virtual void Unbind(); + virtual void Upload(GLsizeiptr size, const void *data, GLenum type = GL_STATIC_DRAW); + virtual void Update(GLintptr offset, GLsizeiptr size, const void *data); +}; + +class VertexBufferObject: public ArrayBuffer +{ +public: + VertexBufferObject(); +}; + +class IndexBufferObject: public ArrayBuffer +{ +public: + IndexBufferObject(); +}; diff --git a/src/graphics/TODO.txt b/src/graphics/TODO.txt new file mode 100644 index 0000000..3a9c1ac --- /dev/null +++ b/src/graphics/TODO.txt @@ -0,0 +1,25 @@ + +GL_MAX_TEXTURE_SIZE +GL_MAX_CUBE_MAP_TEXTURE_SIZE +GL_MAX_RENDERBUFFER_SIZE +GL_ALIASED_POINT_SIZE_RANGE +GL_NUM_COMPRESSED_TEXTURE_FORMATS +GL_COMPRESSED_TEXTURE_FORMATS + +GL_NUM_SHADER_BINARY_FORMATS +GL_SHADER_BINARY_FORMATS + +GL_MAX_TEXTURE_IMAGE_UNITS +GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS +GL_MAX_VARYING_VECTORS +GL_MAX_VERTEX_ATTRIBS +GL_MAX_VERTEX_UNIFORM_VECTORS + +GL_DEPTH_BITS +GL_RENDERBUFFER_DEPTH_SIZE + +GL_FRAMEBUFFER_COMPLETE +GL_VALIDATE_STATUS + +GL_ACTIVE_ATTRIBUTES +GL_ACTIVE_UNIFORMS diff --git a/src/graphics/bin_gpu_prog.cpp b/src/graphics/bin_gpu_prog.cpp new file mode 100644 index 0000000..11af991 --- /dev/null +++ b/src/graphics/bin_gpu_prog.cpp @@ -0,0 +1,146 @@ + +#include +#include +using namespace std; +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/platform_gl.h" +#include "graphics/glsl_prog.h" +#include "graphics/gl_ext.h" +#include "graphics/gl_utils.h" + +#ifdef ANDROID +#define GL_GLEXT_PROTOTYPES 1 +#include +#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES +#define GL_NUM_PROGRAM_BINARY_FORMATS GL_NUM_PROGRAM_BINARY_FORMATS_OES +#define GL_PROGRAM_BINARY_FORMATS GL_PROGRAM_BINARY_FORMATS_OES +#else +#define glProgramBinaryOES glProgramBinary +#define glGetProgramBinaryOES glGetProgramBinary + +#endif + +bool glslProg::CreateFromBinary(int fmt, const void *data, int len){ + if(!(GLExtensions::extFlags&eProgBin)){ + Log("Error: Binary shader program not suported\n"); + return false; + } + + int numFormats=0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS,&numFormats); + Log("numFormats %d\n",numFormats); + int formats[numFormats]={0}; + glGetIntegerv(GL_PROGRAM_BINARY_FORMATS,formats); + Log("format 0: %X\n",formats[0]); + + if(fmt!=formats[0]){ + Log("Error: unsuported shader program format %d\n",fmt); + return false; + } + + if(id) + glDeleteProgram(id); + id = glCreateProgram(); + CheckGLError("pre glProgramBinaryOES",__FILE__,__LINE__); + glProgramBinaryOES(id, fmt, data, len); + + if(CheckGLError("BinaryShaderProgram",__FILE__, __LINE__)){ + Log("bin prog %d error\n",id); + print_log(id); + return false; + } + + GLint link_ok=GL_FALSE; + glGetProgramiv(id,GL_LINK_STATUS,&link_ok); + if(!link_ok){ + Log("bin prog %d GL_LINK_STATUS %d\n",id,link_ok); + print_log(id); + glDeleteProgram(id); + id = 0; + return false; + } + return true; +} + +bool glslProg::Save(const char *name){ + char *progData = 0; + int progLen = 0; + int progFmt = 0; + if(!GetBinaryData(&progFmt,&progData,&progLen)){ + Log("Can't get shader prog data (%s)\n",name); + return false; + } + + Log("ShaderProgram Save(%s): fmt %X, len %d\n",name,progFmt,progLen); + + char path[256]; + + g_fs.GetFilePath((string("shaders/bin/")+name+".bgp").c_str(),path,true); + ofstream out(path,ios::binary); + if(!out){ + Log("Can't create shader prog file (%s)!\n",name); + delete[] progData; + return false; + } + + int ident = (('1'<<24)+('P'<<16)+('G'<<8)+'B'); + out.write((char*)&ident,4); + out.write((char*)&progFmt,4); + out.write((char*)&progLen,4); + out.write(progData,progLen); + out.close(); + delete[] progData; + return true; +} + +bool glslProg::GetBinaryData(int *fmt, char **data, int *len){ + if(!(GLExtensions::extFlags&eProgBin)){ + Log("Error: Binary shader program not suported\n"); + return false; + } + + glGetProgramiv(id,GL_PROGRAM_BINARY_LENGTH,len); + Log("Program binary length %d\n",*len); + + if(len<=0){ + Log("Error: shader program(%d) binary length %d\n",id,*len); + return false; + } + + (*data) = new char[*len]; + //memset(data,0,*len); + + glGetProgramBinaryOES(id, *len, len, (GLenum*)fmt, *data); + Log("ProgramBinary format %X\n",*fmt); + if(CheckGLError("GetBinaryShaderProgram",__FILE__, __LINE__)){ + delete[] (*data); + return false; + } + return true; +} + +bool ShaderFromBinary(int type,const void *data, int len){ + //void glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); + + int numFormats = 0; + glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS,&numFormats); + Log("GL_NUM_SHADER_BINARY_FORMATS %d\n",numFormats); + if(!numFormats){ + return false; + } + int formats[numFormats]={0}; + glGetIntegerv(GL_SHADER_BINARY_FORMATS,formats); + Log("format 0: %X\n",formats[0]); + + if(formats[0]==0x8C0A) + Log("format GL_SGX_BINARY_IMG\n"); + + int fmt = formats[0]; + GLuint sid = glCreateShader(type); + glShaderBinary(1,&sid,fmt,data,len); + + CheckGLError("BinaryShader",__FILE__, __LINE__); + + return false; +} diff --git a/src/graphics/fbo.cpp b/src/graphics/fbo.cpp new file mode 100644 index 0000000..1380954 --- /dev/null +++ b/src/graphics/fbo.cpp @@ -0,0 +1,249 @@ + +#include //exit() + +#include "engine.h" +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/texture.h" +#include "graphics/fbo.h" + +#ifdef ANDROID +#include +#define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES +#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES +#define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES +#define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES +#endif + +bool FrameBufferObject::Create() +{ +#ifndef ANDROID + if(!glGenFramebuffers) + EngineError("FBO::Create gl func is null"); +#endif + glGenFramebuffers(1, &id); + Log("Created FBO %d\n", id); + if(!id) + return false; + glBindFramebuffer(GL_FRAMEBUFFER, id); + + //glBindFramebuffer(GL_FRAMEBUFFER, 0); + return true; +} + +void FrameBufferObject::Bind() +{ + glBindFramebuffer(GL_FRAMEBUFFER, id); + SetupViewport();//remove? +} + +void FrameBufferObject::Unbind() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void FrameBufferObject::SetupViewport() +{ + if(tex) + glViewport(0, 0, tex->width>>lvl, tex->height>>lvl); + else if(depthTex) + glViewport(0, 0, depthTex->width, depthTex->height); +} + +Texture* FrameBufferObject::CreateTexture(int w, int h, int filter) +{ + /*if(tex){ + EngineError("FrameBufferObject::CreateTexture called for fbo with texture\n"); + }*/ + + Log("FBO %d CreateTexture(%d,%d)\n",id,w,h); + tex = new Texture(); + tex->Create(w, h); + tex->SetWrap(GL_CLAMP_TO_EDGE); + tex->SetFilter(filter, filter); + tex->Upload(0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 0); + //glTexImage2D(tex->target, 0, GL_RGBA4, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0); + AttachTexture(tex, 0); + + CheckGLError("FBO::CreateTexture", __FILE__, __LINE__); + return tex; +} + +void FrameBufferObject::Resize(int w, int h) +{ + if(tex) + { + tex->Bind(); + tex->Upload(0, w, h, 0); + //glTexImage2D(tex->target, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + if(depthTex) + { + depthTex->Bind(); + depthTex->Upload(0,w,h,0); + //glTexImage2D(depthTex->target, 0, GL_DEPTH_COMPONENT, w, h, 0,GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + } + glBindTexture(GL_TEXTURE_2D,0); + if(depthid){ + glBindRenderbuffer(GL_RENDERBUFFER, depthid); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, tex->width, tex->height); + } + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + Log("FBO %d Resize(%d,%d)\n",id,w,h); +} + +void FrameBufferObject::AttachTexture(Texture *aTex, int level, bool alloc) +{ + //TODO check !gles or extension +/*#ifdef ANDROID + if(level!=0){ + Log("FBO %d: AttachTexture %d: level!=0(%d)\n",id,aTex->id,level); + } +#endif*/ + lvl = level; + tex = aTex; + //glBindFramebuffer(GL_FRAMEBUFFER, id); + if(alloc) + { + tex->Bind(); + glTexImage2D(tex->target, level, GL_RGBA, tex->width>>level, tex->height>>level, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + //glTexImage2D(tex->target, level, GL_RGBA, tex->width, tex->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex->target, tex->id, level); + //glBindFramebuffer(GL_FRAMEBUFFER, 0); + CheckGLError("FBO::AttachTexture", __FILE__, __LINE__); +} + +void FrameBufferObject::AttachCubemap(Texture *aTex, int face, int level) +{ + tex = aTex; + lvl = level; + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X+face, tex->id, level); + + CheckGLError("FBO::AttachCubemap", __FILE__, __LINE__); +} + +void FrameBufferObject::BindTexture() +{ + tex->Bind(); +} + +Texture *FrameBufferObject::CreateDepthTexture(int w, int h) +{ + if(depthTex){ + EngineError("FrameBufferObject::CreateDepthTexture called for fbo with texture\n"); + } + + if(tex){ + w = tex->width; + h = tex->height; + } + + Log("FBO %d CreateDepthTexture(%d,%d)\n",id,w,h); + + depthTex = new Texture(); + depthTex->Create(w, h); + depthTex->SetWrap(GL_CLAMP_TO_EDGE); + depthTex->SetFilter(GL_NEAREST, GL_NEAREST); +#ifndef ANDROID + //TODO check extension + depthTex->SetFilter(GL_LINEAR, GL_LINEAR); +#endif + //depthTex->Upload(0, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + depthTex->Upload(0, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0); + //glTexImage2D(depthTex->target, 0, GL_DEPTH_COMPONENT, depthTex->width, depthTex->height, 0,GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex->target, depthTex->id, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + CheckGLError("FBO::CreateDepthTexture", __FILE__, __LINE__); + return depthTex; +} + +Texture *FrameBufferObject::CreateDepthStencilTexture(int w, int h) +{ + if(depthTex) + { + EngineError("FrameBufferObject::CreateDepthStencilTexture called for fbo with texture\n"); + } + + if(tex) + { + w = tex->width; + h = tex->height; + } + + depthTex = new Texture(); + depthTex->Create(w, h); + depthTex->SetWrap(GL_CLAMP_TO_EDGE); + depthTex->SetFilter(GL_NEAREST, GL_NEAREST); + + depthTex->Upload(0, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0); + //glTexImage2D(depthTex->target, 0, GL_DEPTH24_STENCIL8, depthTex->width, depthTex->height, 0,GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex->target, depthTex->id, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + CheckGLError("FBO::CreateDepthStencilTexture", __FILE__, __LINE__); + return depthTex; +} + +void FrameBufferObject::BindDepthTexture() +{ + depthTex->Bind(); +} + +void FrameBufferObject::AddDepthBuffer(bool depth24) +{ + glGenRenderbuffers(1, &depthid); + + glBindRenderbuffer(GL_RENDERBUFFER, depthid); + //TODO Check extension + if(depth24) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, tex->width, tex->height); + else + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, tex->width, tex->height); + + glBindFramebuffer(GL_FRAMEBUFFER, id); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,depthid); + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + CheckGLError("FBO::AddDepthBuffer", __FILE__, __LINE__); +} + +void FrameBufferObject::AddDepthStencilBuffer() +{ + glGenRenderbuffers(1, &depthid); + + glBindRenderbuffer(GL_RENDERBUFFER, depthid); + + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex->width, tex->height); + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthid); + glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthid); + + CheckGLError("FBO::AddDepthStencilBuffer", __FILE__, __LINE__); +} + +void FrameBufferObject::Clear(int aMask) +{ + if(!aMask) + return; + int mask=0; + if(aMask&1) + mask|=GL_COLOR_BUFFER_BIT; + if(aMask&2) + mask|=GL_DEPTH_BUFFER_BIT; + if(aMask&4) + mask|=GL_STENCIL_BUFFER_BIT; + glClear(mask); +} diff --git a/src/graphics/fbo.h b/src/graphics/fbo.h new file mode 100644 index 0000000..975c2b1 --- /dev/null +++ b/src/graphics/fbo.h @@ -0,0 +1,38 @@ + +#pragma once + +#include "graphics/texture.h" + +class FrameBufferObject +{ +public: + FrameBufferObject() + { + id = 0; + tex = 0; + lvl = 0; + depthTex = 0; + } + bool Create(); + void Bind(); + static void Unbind(); + void SetupViewport(); + void Resize(int w, int h); + Texture* CreateTexture(int w, int h, int filter = GL_NEAREST); + void AttachTexture(Texture *aTex, int level = 0, bool alloc=false); + void AttachCubemap(Texture *aTex, int face, int level = 0); + void BindTexture(); + Texture* CreateDepthTexture(int w=256, int h=256); + Texture* CreateDepthStencilTexture(int w=256, int h=256); + void BindDepthTexture(); + void AddDepthBuffer(bool depth24=false); + void AddDepthStencilBuffer(); + void Clear(int mask=0); + + Texture *tex; +private: + GLuint id; + GLuint depthid; + int lvl; + Texture *depthTex; +}; diff --git a/src/graphics/gl_ext.cpp b/src/graphics/gl_ext.cpp new file mode 100644 index 0000000..c32f054 --- /dev/null +++ b/src/graphics/gl_ext.cpp @@ -0,0 +1,247 @@ + +#include +#include "log.h" +#include "graphics/gl_ext.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +//#include "gl/wglew.h" + +uint64_t GLExtensions::extFlags; + +const char *GetComprFormatName(int f){ + if(f==0x83F0) + return "RGB_S3TC_DXT1"; + if(f==0x83F1) + return "RGBA_S3TC_DXT1"; + if(f==0x83F2) + return "RGBA_S3TC_DXT3"; + if(f==0x83F3) + return "RGBA_S3TC_DXT5"; + if(f==0x9270) + return "R11_EAC"; + if(f==0x9271) + return "R11_EAC"; + if(f==0x9272) + return "RG11_EAC"; + if(f==0x9273) + return "SIGNED_RG11_EAC"; + if(f==0x8D64) + return "ETC1_RGB8"; + if(f==0x9274) + return "RGB8_ETC2"; + if(f==0x9275) + return "SRGB8_ETC2"; + if(f==0x9276) + return "RGB8_PUNCHTHROUGH_ALPHA1_ETC2"; + if(f==0x9277) + return "SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"; + if(f==0x9278) + return "RGBA8_ETC2_EAC"; + if(f==0x9279) + return "SRGB8_ALPHA8_ETC2_EAC"; + if(f==0x8E8C) + return "RGBA_BPTC_UNORM"; + if(f==0x8E8D) + return "SRGB_ALPHA_BPTC_UNORM"; + if(f==0x8E8E) + return "RGB_BPTC_SIGNED_FLOAT"; + if(f==0x8E8F) + return "RGB_BPTC_UNSIGNED_FLOAT"; + if(f==0x8C00) + return "RGB_PVRTC_4BPPV1_IMG"; + if(f==0x8C01) + return "RGB_PVRTC_2BPPV1_IMG"; + if(f==0x8C02) + return "RGBA_PVRTC_4BPPV1_IMG"; + if(f==0x8C03) + return "RGBA_PVRTC_2BPPV1_IMG"; + return 0; +} + +void GLExtensions::Init() +{ + extFlags = 0; + + const char *glVer = (const char *)glGetString(GL_VERSION); + if(strstr(glVer,"ES")) + extFlags |= eGLES; + if(strstr(glVer,"Core")) + extFlags |= eCore; + + int numExts = 0; + const char *extString = 0; + const char *t = 0; + char ext[256]={0}; + + if(extFlags&eCore){ +#ifndef ANDROID + //gl 3.0 way + glGetIntegerv(GL_NUM_EXTENSIONS,&numExts); +#endif + }else{ + extString = (const char *)glGetString(GL_EXTENSIONS); + t = extString; + + const char *end = 0; + while(*t){ + end = strstr(t," "); + if(end){ + strncpy(ext,t,end-t); + ext[end-t]=0; + } + else + break; + t=end+1; + numExts++; + } + t = extString; + } + Log("Found %d extensions\n",numExts); + + for(int i=0; i +void GLExtensions::InitVAO(){ + Log("InitVAO\n"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); + Log("glBindVertexArray %p\n",glBindVertexArray); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); + glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); +} +#else +void GLExtensions::InitVAO(){Log("InitVAO null\n");} +#endif diff --git a/src/graphics/gl_ext.h b/src/graphics/gl_ext.h new file mode 100644 index 0000000..38f3582 --- /dev/null +++ b/src/graphics/gl_ext.h @@ -0,0 +1,41 @@ + +#pragma once + +#include + +enum GLExtFlags{ + eGLES=1, + eCore=2, + eVertex_array_object=4, + eTexture_npot=8, + eTexture_compression_s3tc=16, + ePacked_depth_stencil=32, + eTexture_filter_anisotropic=64, + eCompressed_ETC1_RGB8_texture=128, + eDepth24=256, + eDepth32=512, + eElement_index_uint=1024, + eFBO_rgb8_rgba8=2048, + eTexture_format_BGRA8888=4096, + eProgBin=8192, + eDepth_texture=16384 +}; + +class GLExtensions{ +public: + static void Init(); + static void InitVAO(); + + static uint64_t extFlags; +}; + +#ifdef ANDROID +#include "graphics/platform_gl.h" +#include +extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray; +extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; +extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays; +extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray; + +#define GL_HALF_FLOAT GL_HALF_FLOAT_OES +#endif diff --git a/src/graphics/gl_utils.cpp b/src/graphics/gl_utils.cpp new file mode 100644 index 0000000..fff0346 --- /dev/null +++ b/src/graphics/gl_utils.cpp @@ -0,0 +1,37 @@ + +#include "cstdlib" +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" + +const char *GetGLErrorString(int err) +{ + switch(err) + { + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + default: + return "???"; + } +} + +int CheckGLError(const char *func, const char* file, int line) +{ + GLenum err = glGetError(); + if(err) + { + Log("gl Error %x(%s) on %s %s(%d)\n",err,GetGLErrorString(err),func,file,line); + //TODO glError Fatal option + //exit(err); + } + return err; +} + diff --git a/src/graphics/gl_utils.h b/src/graphics/gl_utils.h new file mode 100644 index 0000000..c64adbd --- /dev/null +++ b/src/graphics/gl_utils.h @@ -0,0 +1,5 @@ + +#pragma once + +int CheckGLError(const char *func, const char* file, int line); + diff --git a/src/graphics/glsl_prog.cpp b/src/graphics/glsl_prog.cpp new file mode 100644 index 0000000..5d840a9 --- /dev/null +++ b/src/graphics/glsl_prog.cpp @@ -0,0 +1,252 @@ + +#include +#include +#include +using namespace std; + +#include + +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/platform_gl.h" +#include "graphics/glsl_prog.h" +#include "graphics/gl_utils.h" + +glslProg::glslProg() +{ + id = 0; + u_mvpMtx = -1; + u_modelMtx = -1; + u_invModelMtx = -1; + u_cameraPos = -1; + u_color = -1; +} + +glslProg::glslProg(const char *vert, const char *frag) +{ + id = 0; + u_mvpMtx = -1; + u_modelMtx = -1; + u_invModelMtx = -1; + u_cameraPos = -1; + u_color = -1; + if(!CreateProgram(vert, frag,"")){ + Log( "Shader program ctor error!\n"); + } +} + +glslProg::~glslProg(){ + if(id) + glDeleteProgram(id); + id = 0; +} + +bool glslProg::CreateFromFile(const char *vFileName, const char *fFileName) +{ + string path = "shaders/"+string(vFileName)+".vs"; + char *vs = g_fs.ReadAll(path.c_str()); + if(!vs){ + Log("Vertex shader file %s missing\n", vFileName); + return false; + } + + path = "shaders/"+string(fFileName)+".fs"; + char *fs = g_fs.ReadAll(path.c_str()); + if(!fs){ + Log("Fragment shader file %s missing\n", fFileName); + delete[] vs; + return false; + } + + bool result = CreateProgram(vs, fs,""); + + delete[] vs; + delete[] fs; + + if(!result) + Log("Error in program %s/%s\n",vFileName,fFileName); + return result; +} + +bool glslProg::CreateFromFile(const char *vFileName, const char *fFileName, const char *flags) +{ + string path = "shaders/"+string(vFileName)+".vs"; + char *vs = g_fs.ReadAll(path.c_str()); + if(!vs){ + Log("Vertex shader file %s missing\n", vFileName); + return false; + } + + path = "shaders/"+string(fFileName)+".fs"; + char *fs = g_fs.ReadAll(path.c_str()); + if(!fs){ + Log("Fragment shader file %s missing\n", fFileName); + delete[] vs; + return false; + } + + bool result = CreateProgram(vs, fs, flags); + + delete[] vs; + delete[] fs; + + if(!result) + Log("Error in program %s/%s (%s)\n",vFileName,fFileName,flags); + return result; +} + +void glslProg::print_log(GLuint object) +{ + GLint log_length=0; + if(glIsShader(object)){ + glGetShaderiv(object,GL_INFO_LOG_LENGTH,&log_length); + Log("Shader log_length %d\n",log_length); + }else if (glIsProgram(object)){ + glGetProgramiv(object,GL_INFO_LOG_LENGTH,&log_length); + Log("Program log_length %d\n",log_length); + }else{ + Log("print_log: Not a shader or a program\n"); + return; + } + if(!log_length) + return; + char* log = new char[log_length]; + + if(glIsShader(object)) + glGetShaderInfoLog(object,log_length,NULL,log); + else if (glIsProgram(object)) + glGetProgramInfoLog(object,log_length,NULL,log); + + Log("%s\n",log); + delete[] log; +} + +GLuint glslProg::CreateShader(const char *src, GLint type, const char *flags) +{ + GLuint sid = glCreateShader(type); + const char *strings[]={flags,src}; + glShaderSource(sid,2,strings,NULL); + glCompileShader(sid); + GLint compile_ok = GL_FALSE; + glGetShaderiv(sid,GL_COMPILE_STATUS, &compile_ok); + if(!compile_ok){ + if(type==GL_VERTEX_SHADER) + Log("vert: "); + else + Log("frag: "); + print_log(sid); + glDeleteShader(sid); + return 0; + } + return sid; +} + +bool glslProg::CreateProgram(const char *vert, const char *frag, const char *flags) +{ + GLuint vs = CreateShader(vert, GL_VERTEX_SHADER, flags); + //CheckGLError("CreateShader", __FILE__, __LINE__); + if(!vs) + return false; + GLuint fs = CreateShader(frag, GL_FRAGMENT_SHADER, flags); + if(!fs){ + glDeleteShader(vs); + return false; + } + if(id) + glDeleteProgram(id); + id = glCreateProgram(); + //CheckGLError("glCreateProgram", __FILE__, __LINE__); + + glBindAttribLocation(id,0,"a_position"); + glBindAttribLocation(id,1,"a_normal"); + glBindAttribLocation(id,2,"a_uv"); + glBindAttribLocation(id,3,"a_uv2");//TODO check old projects + glBindAttribLocation(id,4,"a_tangent");//TODO check old projects + + //CheckGLError("glBindAttribLocation", __FILE__, __LINE__); + + glAttachShader(id,vs); + glAttachShader(id,fs); + + glLinkProgram(id); + //CheckGLError("glLinkProgram", __FILE__, __LINE__); + + glDeleteShader(vs); + glDeleteShader(fs); + + GLint link_ok=GL_FALSE; + glGetProgramiv(id, GL_LINK_STATUS, &link_ok); + if(!link_ok){ + Log("prog %d GL_LINK_STATUS %d\n",id,link_ok); + print_log(id); + glDeleteProgram(id); + id = 0; + return false; + } + +#if 0 + int uniformsCount = 0; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformsCount); + Log("Shader: active uniforms count %d\n", uniformsCount); + for(int i=0; i + +class glslProg +{ +public: + glslProg(); + glslProg(const char *vert, const char *frag); + ~glslProg(); + + GLint u_mvpMtx; + GLint u_modelMtx; + GLint u_invModelMtx; + GLint u_cameraPos; + GLint u_color; + + bool CreateFromFile(const char *vFileName, const char *fFileName); + bool CreateFromFile(const char *vFileName, const char *fFileName, const char *flags); + bool CreateFromBinary(int fmt, const void *data, int len); + bool Use(); + GLint GetUniformLoc(const char *uname); + GLint GetAttribLoc(const char *aname); + bool GetBinaryData(int *fmt, char **data, int *len); + void UniformVec4(int loc, const glm::vec4 &vec); + void UniformMat4(int loc, const glm::mat4 &mtx); + void UniformTex(const char *name, int unit); + bool Save(const char *name); +private: + GLuint id; + GLuint CreateShader(const char *text, GLint type, const char *flags); + bool CreateProgram(const char *vert, const char *frag, const char *flags); + void print_log(GLuint object); +}; diff --git a/src/graphics/platform_gl.h b/src/graphics/platform_gl.h new file mode 100644 index 0000000..d5a7cde --- /dev/null +++ b/src/graphics/platform_gl.h @@ -0,0 +1,16 @@ + +#pragma once + +#ifndef ANDROID +#if !USE_CORE_GL_HEADER + #define GLEW_NO_GLU 1 + #define GLEW_STATIC 1 + #include +#else + #define GL_GLEXT_PROTOTYPES 1 + #include "GL/glcorearb.h" + #include "GL/gl.h" +#endif +#else + #include +#endif diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp new file mode 100644 index 0000000..a95d618 --- /dev/null +++ b/src/graphics/texture.cpp @@ -0,0 +1,309 @@ + +#include + +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/texture.h" + +void DecompressDXT(Texture *tex, const GLubyte *data, GLubyte *out, int texSize, int inFormat); +void UploadDXT(Texture *tex, const GLubyte *data, int texSize, int inFormat); + +Texture::Texture() +{ + width = 0; + height = 0; + id = 0; + target = GL_TEXTURE_2D; + type = GL_UNSIGNED_BYTE; + fmt = GL_RGB; + infmt = GL_RGB; +} + +Texture::~Texture(){ + if(id) + glDeleteTextures(1,&id); +} + +bool Texture::Create(int w, int h) +{ + glGenTextures(1, &id); + if(!id) + return false; + //LOG("Created texture(%dx%d) %d\n", w, h, id); + glBindTexture(target, id); + /* + glTexImage2D(target, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, NULL); + if(glGetError()) + return false; + */ + width = w; + height = h; + SetWrap(GL_CLAMP_TO_EDGE); + SetFilter(GL_NEAREST, GL_NEAREST); + + //glBindTexture(GL_TEXTURE_2D, 0); + return true; +} + +void Texture::Bind() +{ + glBindTexture(target, id); +} + +void Texture::SetWrap(int wrap) +{ + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); +} + +void Texture::SetFilter(int min, int mag) +{ + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag); +} + +//TODO fix levels +void Texture::Upload(GLint level, GLuint format, const void *data) +{ + fmt=format; + infmt=format; + + glTexImage2D(target, level, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); +} + +void Texture::Upload(GLint level, GLuint iformat, GLuint format, GLuint tp, const void *data) +{ + infmt=iformat; + fmt=format; + type=tp; + + glTexImage2D(target, level, iformat, width>>level, height>>level, 0, format, type, data); +} + +void Texture::Upload(int lvl, int w, int h, const void *data) +{ + if(lvl==0){ + width=w; + height=h; + } + glTexImage2D(target, lvl, infmt, w, h, 0, fmt, type, data); +} + +void Texture::UploadCompressed(GLuint iformat, int size, const void *data) +{ +#ifdef ANDROID + if(iformat==GL_COMPRESSED_RGB_S3TC_DXT1|| + iformat==GL_COMPRESSED_RGBA_S3TC_DXT5) + { + UploadDXT(this, (const GLubyte*)data, size, iformat); + return; + } +#endif + infmt = iformat; + glCompressedTexImage2D(target, 0, iformat, width, height, 0, size, data); +} + +void ResampleBGR(uint8_t *data, int size) +{ + int t; + for(int i=0; iUpload(0, format, newData); + delete[] newData; +} + +void DecompressDXT(Texture *tex, const GLubyte *data, GLubyte *out, int texSize, int inFormat) +{ + //Log("Software dxt decompressor: s %d f %d %dx%d\n",texSize,inFormat,tex->width,tex->height); + int blockSize; + int colOffs; + int stride; + int outputStride; + if(inFormat==GL_COMPRESSED_RGB_S3TC_DXT1) + { + blockSize = 8; + stride = 3; + outputStride = tex->width*3; + colOffs = 0; + } + else //dxt5 + { + blockSize = 16; + stride = 4; + outputStride = tex->width*4; + colOffs = 8; + } + + for(int i=0; i<(int)texSize/blockSize; i++) + { + GLushort col0 = *(GLushort*)(data+(i*blockSize+colOffs)); + GLushort col1 = *(GLushort*)(data+i*blockSize+colOffs+2); + uint64_t code = *(GLuint*)(data+i*blockSize+colOffs+4); + int offs = i*4*stride + i/(tex->width/4)*outputStride*3; + + GLubyte r0 = (col0>>11)&0x1F; + GLubyte g0 = (col0>>5)&0x3F; + GLubyte b0 = col0&0x1F; + r0 = (r0<<3)|(r0>>2); + g0 = (g0<<2)|(g0>>4); + b0 = (b0<<3)|(b0>>2); + GLubyte r1 = (col1>>11)&0x1F; + GLubyte g1 = (col1>>5)&0x3F; + GLubyte b1 = col1&0x1F; + r1 = (r1<<3)|(r1>>2); + g1 = (g1<<2)|(g1>>4); + b1 = (b1<<3)|(b1>>2); + GLubyte r,g,b; + + GLubyte poscode; + for(int y=0;y<4;y++) + { + for(int x=0;x<4;x++) + { + poscode = code>>2*(4*y+x)&3; + if(col0>col1) + { + switch(poscode) + { + case 0: + r = r0; + g = g0; + b = b0; + break; + case 1: + r = r1; + g = g1; + b = b1; + break; + case 2: + r = (2*r0+r1)/3; + g = (2*g0+g1)/3; + b = (2*b0+b1)/3; + break; + case 3: + r = (r0+2*r1)/3; + g = (g0+2*g1)/3; + b = (b0+2*b1)/3; + break; + } + } + else + { + switch(poscode) + { + case 0: + r = r0; + g = g0; + b = b0; + break; + case 1: + r = r1; + g = g1; + b = b1; + break; + case 2: + r = (r0+r1)/2; + g = (g0+g1)/2; + b = (b0+b1)/2; + break; + case 3: + r = g = b = 0; + break; + } + } + + out[offs+y*outputStride+x*stride] = r; + out[offs+y*outputStride+x*stride+1] = g; + out[offs+y*outputStride+x*stride+2] = b; + } + } + + if(inFormat==GL_COMPRESSED_RGB_S3TC_DXT1) + continue; + + GLubyte a0 = *(data+i*blockSize); + GLubyte a1 = *(data+i*blockSize+1); + code = *(uint64_t*)(data+i*blockSize); + GLubyte a[8]; + + a[0] = a0; + a[1] = a1; + + /*if(a0>a1) + { + a[2] = (6.0f*a0 + a1)/7.0f; + a[3] = (5.0f*a0 + 2.0f*a1)/7.0f; + a[4] = (4.0f*a0 + 3.0f*a1)/7.0f; + a[5] = (3.0f*a0 + 4.0f*a1)/7.0f; + a[6] = (2.0f*a0 + 5.0f*a1)/7.0f; + a[7] = (a0 + 6.0f*a1)/7.0f; + } + else + { + a[2] = (4.0f*a0+a1)/5.0f; + a[3] = (3.0f*a0+2.0f*a1)/5.0f; + a[4] = (2.0f*a0+3.0f*a1)/5.0f; + a[5] = (a0+4.0f*a1)/5.0f; + a[6] = 0; + a[7] = 1.0f; + }*/ + if( a0 <= a1 ) + { + // use 5-alpha codebook + for( int j = 1; j < 5; ++j ) + a[1 + j] = ( ( ( 5 - j )*a0 + j*a1 )/5 ); + a[6] = 0; + a[7] = 255; + } + else + { + // use 7-alpha codebook + for( int j = 1; j < 7; ++j ) + a[1 + j] = ( ( ( 7 - j )*a0 + j*a1 )/7 ); + } + + for(int y=0;y<4;y++) + { + for(int x=0;x<4;x++) + { + poscode = (code >> (16+3*(4*y+x))) & 0x07; + out[offs+y*outputStride+x*stride+3] = a[poscode]; + } + } + } +} diff --git a/src/graphics/texture.h b/src/graphics/texture.h new file mode 100644 index 0000000..28f362c --- /dev/null +++ b/src/graphics/texture.h @@ -0,0 +1,49 @@ + +#pragma once +//TODO split interface and implementation + +#include "graphics/platform_gl.h" + +#ifdef ANDROID +#include "GLES2/gl2ext.h" +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_RGBA16F GL_RGBA +#define GL_HALF_FLOAT GL_HALF_FLOAT_OES +//GL_EXT_texture_format_BGRA8888 +#define GL_BGRA GL_BGRA_EXT +#else +#include "GL/glext.h" +#endif +#define GL_COMPRESSED_RGB_S3TC_DXT1 GL_COMPRESSED_RGB_S3TC_DXT1_EXT +#define GL_COMPRESSED_RGBA_S3TC_DXT5 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + +#define FMT_BGR8 0x321 +#define FMT_ARGB8 0x4321 +#define FMT_V8U8 0xF8E8 + +void ResampleBGR(uint8_t *data, int size); +void ResampleBGRA(uint8_t *data, int size); + +class Texture +{ +public: + int width; + int height; + GLuint id; + GLuint target; + GLuint type; + GLuint fmt; + GLuint infmt; + + Texture(); + ~Texture(); + + bool Create(int w, int h); + void Bind(); + void SetWrap(int wrap); + void SetFilter(int min, int mag); + void Upload(GLint level, GLuint format, const void *data); + void Upload(GLint level, GLuint iformat, GLuint format, GLuint tp, const void *data); + void Upload(GLint level, int w, int h, const void *data); + void UploadCompressed(GLuint iformat, int size, const void *data); +}; diff --git a/src/graphics/vao.cpp b/src/graphics/vao.cpp new file mode 100644 index 0000000..4bb3337 --- /dev/null +++ b/src/graphics/vao.cpp @@ -0,0 +1,50 @@ + +#include "vao.h" +#include "graphics/platform_gl.h" + +/*#ifdef ANDROID +#define GL_GLEXT_PROTOTYPES 1 +#include +#define glBindVertexArray glBindVertexArrayOES +#define glDeleteVertexArrays glDeleteVertexArraysOES +#define glGenVertexArrays glGenVertexArraysOES +#endif*/ +#include "graphics/gl_ext.h" + +VertexArrayObject::VertexArrayObject() +{ + id = 0; +} + +VertexArrayObject::~VertexArrayObject() +{ + if(id) + glDeleteVertexArrays(1,&id); +} + +bool VertexArrayObject::Create() +{ + glGenVertexArrays(1,&id); + glBindVertexArray(id); + + glBindVertexArray(0); + return true; +} + +void VertexArrayObject::Bind() +{ + glBindVertexArray(id); +} + +void VertexArrayObject::Unbind() +{ + glBindVertexArray(0); +} + +void VertexArrayObject::SetAttribute(int index,int size,int type,bool norm, int stride, void *vert) +{ + glBindVertexArray(id); + glEnableVertexAttribArray(index); + glVertexAttribPointer(index,size,type,norm,stride,vert); +} + diff --git a/src/graphics/vao.h b/src/graphics/vao.h new file mode 100644 index 0000000..66aaf0e --- /dev/null +++ b/src/graphics/vao.h @@ -0,0 +1,20 @@ + +#pragma once + +class VertexBufferObject; + +class VertexArrayObject +{ +public: + VertexArrayObject(); + ~VertexArrayObject(); + + bool Create(); + void Bind(); + static void Unbind(); + void SetAttribute(int index,int size,int type,bool norm, int stride, void *vert); + //void SetVertsVBO(int index,int size,int type,bool norm, int stride, int offset, VertexBufferObject &vbo); + + unsigned int id; +}; + diff --git a/src/graphics/vbo.cpp b/src/graphics/vbo.cpp new file mode 100644 index 0000000..3be9bbd --- /dev/null +++ b/src/graphics/vbo.cpp @@ -0,0 +1,51 @@ + +#include "log.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/ArrayBuffer.h" + +ArrayBuffer::ArrayBuffer(){ + id = 0; + target = GL_ARRAY_BUFFER; +} + +ArrayBuffer::~ArrayBuffer(){ + if(id) + glDeleteBuffers(1, &id); +} + +void ArrayBuffer::Create(){ + glGenBuffers(1, &id); +} + +void ArrayBuffer::Bind(){ + glBindBuffer(target, id); +} + +void ArrayBuffer::Unbind(){ + glBindBuffer(target, 0); +} + +void ArrayBuffer::Upload(GLsizeiptr size, const void *data, GLenum type){ + glBindBuffer(target, id); + glBufferData(target, size, data, type); + glBindBuffer(target, 0); + CheckGLError("ArrayBuffer::Upload", __FILE__, __LINE__); +} + +void ArrayBuffer::Update(GLintptr offset, GLsizeiptr size, const void *data){ + glBindBuffer(target, id); + glBufferSubData(target, offset, size, data); + glBindBuffer(target, 0); + CheckGLError("ArrayBuffer::Update", __FILE__, __LINE__); +} + +//VBO +VertexBufferObject::VertexBufferObject():ArrayBuffer(){ + target = GL_ARRAY_BUFFER; +} + +//IBO +IndexBufferObject::IndexBufferObject():ArrayBuffer(){ + target = GL_ELEMENT_ARRAY_BUFFER; +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..1e011e9 --- /dev/null +++ b/src/input.h @@ -0,0 +1,25 @@ + +#pragma once + +#define IN_PRESS 0 +#define IN_RELEASE 1 +#define IN_MOVE 2 + +#ifdef ANDROID +#define IN_KEY_W 1 +#define IN_KEY_S 2 +#define IN_KEY_A 3 +#define IN_KEY_D 4 + +#else +#define GLFW_INCLUDE_NONE +#include +#define IN_KEY_W GLFW_KEY_W +#define IN_KEY_S GLFW_KEY_S +#define IN_KEY_A GLFW_KEY_A +#define IN_KEY_D GLFW_KEY_D + +#define IN_KEY_PRESS GLFW_PRESS +#define IN_KEY_RELEASE GLFW_RELEASE +#endif + diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 0000000..e2b5fda --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,57 @@ + +#include +#include +#include +using namespace std; +#include "log.h" +#include "system/FileSystem.h" + +ofstream enginelog; +int logMode = eLogBoth; + +void LogInit() +{ + logMode = eLogBoth; + + char path[256]; +#ifdef WIN32 + snprintf(path,256,"%s/log.txt", g_fs.gamedir.c_str()); +#else + snprintf(path,256,"%s/nenuzhno-engine/%s/log.txt", getenv("EXTERNAL_STORAGE"), g_fs.gamedir.c_str()); +#endif + enginelog.open(path); + Log("LogInit() path = %s\n",path); + if(!enginelog) + { + SYS_LOG("Can't create engine log file!\n"); + return; + } +} + +void Log(const char *msg, ...) +{ + va_list vl; + va_start(vl,msg); + + char buff[4096]; + vsnprintf(buff,4095, msg, vl); + buff[4095]=0; + + if(logMode&eLogToConsole) + SYS_LOG("%s",buff); + if(logMode&eLogToFile && enginelog){ + enginelog << buff; + enginelog.flush(); + } + + va_end(vl); +} + +void SetLogMode(int m) +{ + if(meLogBoth){ + Log("Error: unknown log mode %d\n",m); + return; + } + logMode = m; +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..6c30a57 --- /dev/null +++ b/src/log.h @@ -0,0 +1,25 @@ + +#pragma once + +#ifndef ANDROID + #include + #define SYS_LOG(...) ((void)printf( __VA_ARGS__)) +#else + #include + #define SYS_LOG(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "nenuzhno", __VA_ARGS__)) +#endif +#define LOG(...) ((void)Log( __VA_ARGS__)) + +enum eLogMode{ + eLogNone=0, + eLogToConsole=1, + eLogToFile=2, + eLogBoth=3 +}; + +void SetLogMode(int m); +void Log(const char *msg, ...); +//av_printf_format(3, 4); + +void LogInit(); + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0144ea4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,216 @@ + +#include +#include +#include + +using namespace std; + +#define GLEW_STATIC 1 +#define GLEW_NO_GLU 1 +#include +#include +#include "log.h" +#include "game/IGame.h" +#include "system/FileSystem.h" + +void Created(); +void Changed(int w, int h); +void Draw(); +void OnKey(int key, int scancode, int action, int mods); +void OnTouch(float tx, float ty, int ta, int tf); +void OnMouseMove(float mx, float my); +void OnScroll(float sx, float sy); + +GLFWwindow *window; +void EngineSwapBuffers() +{ + glfwSwapBuffers(window); +} + +void EngineError(const char *message) +{ + LOG("EngineError!!! (%s)\n", message); + exit(-1); +} + +void EngineQuit(){ + glfwSetWindowShouldClose(window, 1); +} + +void EnableCursor(bool state) +{ + if(state) + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + else + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); +} + +double GetTime() +{ + return glfwGetTime(); +} + +void FramebufferSizeCallback(GLFWwindow *win, int w, int h) +{ + Changed(w,h); + Draw(); + glfwSwapBuffers(win); +} + +void error_callback(int error, const char* description) +{ + Log("glfw error %d : %s\n",error,description); +} + +void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods) +{ + //if(key==GLFW_KEY_ESCAPE && action==GLFW_PRESS) + // glfwSetWindowShouldClose(win, 1); + + OnKey(key, scancode, action, mods); +} + +int cursorPosX = 0; +int cursorPosY = 0; +int cursorState = 1; + +void cursorPos_callback(GLFWwindow* win, double x, double y) +{ + //LOG("cursor %f %f\n", x, y); + cursorPosX = x; + cursorPosY = y; + if(!cursorState) + OnTouch(cursorPosX,cursorPosY,2,0); + OnMouseMove(x,y); +} + +void mouseButton_callback(GLFWwindow* win, int button, int action, int mods) +{ + cursorState = (action+1)%2; + //LOG("mouse button %d %d %d\n", button, action, mods); + OnTouch(cursorPosX,cursorPosY,cursorState,0); +} + +void scroll_callback(GLFWwindow* win, double xoffset, double yoffset) +{ + OnScroll(xoffset, yoffset); +} + +int main(int argc, char* argv[]) +{ + bool fullscreen = false; + bool gles = false; + bool gl_core = false; + int glv[2] = {2,0}; + bool vsync = false; + const char *cgamedir=0; + int msaa = 0; + GLuint scrWidth = 800; + GLuint scrHeight = 480; + + for(int i=1; i - Enable multisampling with \n" + <<"-w -h - Screen size\n" + <<"-glcore x.x - Init core OpenGL context" + <<"-gles x.x (experimental) - Init gles context\n" + <<"-vsync - Enable vertical sync\n" + <<"-game - Set gamdir\n" + <<"-help - Print this message\n"; + } + } + + glfwSetErrorCallback(error_callback); + if(!glfwInit()) + { + cerr<<"Init error\n"; + return -1; + } + + if(gl_core||gles){ + if(gl_core){ + Log("Init core gl context (%d.%d)\n",glv[0],glv[1]); + if(glv[0]>=3&&glv[1]>=2) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + }else{ + Log("Init gles context (%d.%d)\n",glv[0],glv[1]); + glfwWindowHint(GLFW_CLIENT_API,GLFW_OPENGL_ES_API); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, glv[0]); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, glv[1]); + } + + glfwWindowHint(GLFW_SAMPLES, msaa); + + if(!fullscreen) + window = glfwCreateWindow(scrWidth,scrHeight,"Nenuzhno engine",NULL,NULL); + else + window = glfwCreateWindow(scrWidth,scrHeight,"Nenuzhno engine",glfwGetPrimaryMonitor(),NULL); + + if(!window){ + Log("CreateWindow error\n"); + glfwTerminate(); + return -1; + } + + glfwMakeContextCurrent(window); + if(!vsync) + glfwSwapInterval(0); + else + glfwSwapInterval(1); + + GLenum err = glewInit(); + if(err != GLEW_OK){ + Log("GLEW Error: %s\n", glewGetErrorString(err)); + } + glfwSetFramebufferSizeCallback(window,FramebufferSizeCallback); + glfwSetKeyCallback(window, key_callback); + glfwSetCursorPosCallback(window, cursorPos_callback); + glfwSetMouseButtonCallback(window, mouseButton_callback); + glfwSetScrollCallback(window, scroll_callback); + + Log("glfw %s\n", glfwGetVersionString()); + GameInit(); + if(!cgamedir && pGame) + cgamedir = pGame->GetGamedir(); + + g_fs.Init(cgamedir); + LogInit(); + //InputSystem Init + Created(); + FramebufferSizeCallback(window,scrWidth,scrHeight);//Changed + + while(!glfwWindowShouldClose(window)) + { + //InputSystem Update + Draw(); + glfwSwapBuffers(window); + glfwPollEvents(); + } + + Log("Done\n"); + glfwDestroyWindow(window); + glfwTerminate(); + return 0; +} diff --git a/src/network/network.cpp b/src/network/network.cpp new file mode 100644 index 0000000..ace5a8d --- /dev/null +++ b/src/network/network.cpp @@ -0,0 +1,196 @@ + +#include +#include "network.h" +#include "log.h" + +IPAddress::IPAddress(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t p) +{ + Set(a,b,c,d,p); +} + +uint8_t IPAddress::Get(int i) const +{ + return ((uint8_t *)&ip)[3-i]; +} + +void IPAddress::Set(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t p) +{ + ip = ( a << 24 ) | ( b << 16 ) | ( c << 8 ) | d; + port = p; +} + +NetworkManager::NetworkManager() +{ + initialized = false; + Serialize = 0; + Deserialize = 0; + clientId = 0; + fromLastClientUpdate = 0; +} + +bool NetworkManager::Init() +{ + if(initialized) + return true; + + Log("Init network\n"); + initialized = true; +#ifdef WIN32 + WSAData data; + return WSAStartup(MAKEWORD(2,2), &data) == NO_ERROR; +#else + return true; +#endif +} + +void NetworkManager::Shutdown() +{ +#ifdef WIN32 + WSACleanup(); +#endif + initialized = false; +} + +bool NetworkManager::HostServer(uint16_t port) +{ + if(!serverSocket.Open(port)){ + Log("Can't open server socket\n"); + return false; + } + Log("Open server socket %d\n",port); + return true; +} + +void NetworkManager::UpdateServer(float deltaTime) +{ + if(!initialized) + return; + + if(!serverSocket.isOpen()) + return; + + while(true){ + char packet_data[256]; + uint32_t max_packet_size = sizeof(packet_data); + + IPAddress from_addr; + int rec_bytes = serverSocket.Receive(from_addr, packet_data, max_packet_size); + + if(!rec_bytes) + break; + + //LOG("Received packet from %d.%d.%d.%d:%d\n%s\n", from_addr.Get(3), from_addr.Get(2), from_addr.Get(1), from_addr.Get(0), from_addr.port, packet_data); + + if(*(uint32_t*)packet_data != PROTOCOL_ID) + continue; + + if(packet_data[4]=='c' && packet_data[5]=='r'){ + Log("Got connect request\n"); + + char request[8]; + *(uint32_t*)request = PROTOCOL_ID; + request[4] = 'c'; + request[5] = 's'; + request[6] = 1;//client_id + request[7] = 0; + + if(!serverSocket.Send(from_addr, request, 8)){ + Log("Can't send connect success message\n"); + } + } + + if(packet_data[4] == 'u')//update + { + if(Deserialize){ + int message_size = rec_bytes-5; + Deserialize(packet_data+5, &message_size); + } + } + } +} + +bool NetworkManager::ConnectToServer(IPAddress address, uint16_t clientPort) +{ + if(!initialized) + return false; + + if(!clientSocket.isOpen()){ + if(!clientSocket.Open(clientPort)){ + Log("Can't open client socket\n"); + return false; + } + Log("Open client socker %d\n",clientPort); + } + + char request[8]; + *(uint32_t*)request = PROTOCOL_ID; + request[4]='c'; + request[5]='r'; + request[6]=0; + request[7]=0; + + Log("Try connect to %d.%d.%d.%d:%d\n",address.Get(0),address.Get(1),address.Get(2),address.Get(3),address.port); + return clientSocket.Send(address, request, 8); + + //return true; +} + +void NetworkManager::UpdateClient(float deltaTime) +{ + if(!initialized) + return; + + if(!clientSocket.isOpen()){ + LOG("UpdateClient clientSocket closed!\n"); + return; + } + + //receive + while(true) + { + char packet_data[256]; + uint32_t max_packet_size = sizeof(packet_data); + + IPAddress from_addr; + int rec_bytes = clientSocket.Receive(from_addr, packet_data, max_packet_size); + + if(!rec_bytes) + break; + + LOG("Received packet from %d.%d.%d.%d:%d\n%s\n", from_addr.Get(3), from_addr.Get(2), from_addr.Get(1), from_addr.Get(0), from_addr.port, packet_data); + + if(*(uint32_t*)packet_data != PROTOCOL_ID) + continue; + + if(packet_data[4]=='c' && packet_data[5]=='s') + { + serverAddress = from_addr; + clientId = packet_data[6]; + LOG("Connect success (%d)\n", clientId); + } + } + + //send + if(clientId && Serialize) + { + fromLastClientUpdate += deltaTime; + if(fromLastClientUpdate>(1.0f/30)) + { + fromLastClientUpdate=0; + char packet[256]; + int size = 0;//empty + *(uint32_t*)packet = PROTOCOL_ID; + packet[4] = 'u'; + packet[5] = clientId; + Serialize(packet+6, &size); + + clientSocket.Send(serverAddress, packet, 6+size); + } + } +} + +void NetworkManager::SetSerializeCallbacks(SerializeFun ser, SerializeFun deser) +{ + Serialize = ser; + Deserialize = deser; +} diff --git a/src/network/network.h b/src/network/network.h new file mode 100644 index 0000000..72499d9 --- /dev/null +++ b/src/network/network.h @@ -0,0 +1,53 @@ + +#pragma once + +#ifdef WIN32 + #include +#else//unix + #include + #include + #include +#endif + +#include + +#define PROTOCOL_ID 0x14881337 + +struct IPAddress +{ + uint32_t ip; + uint16_t port; + + IPAddress(){ip=0;port=0;} + IPAddress(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t p); + uint8_t Get(int i) const; + void Set(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t p); +}; + +#include "network/udp_socket.h" + +typedef void (* SerializeFun)(char *, int *); + +//TODO: clients list +class NetworkManager +{ + bool initialized; + UDPSocket serverSocket; + IPAddress serverAddress; + UDPSocket clientSocket; + int clientId; + SerializeFun Serialize; + SerializeFun Deserialize; + float fromLastClientUpdate; +public: + NetworkManager(); + + bool Init(); + void Shutdown(); + bool HostServer(uint16_t port); + void UpdateServer(float deltaTime); + bool ConnectToServer(IPAddress address, uint16_t clientPort); + void UpdateClient(float deltaTime); + + void SetSerializeCallbacks(SerializeFun ser, SerializeFun deser); +}; diff --git a/src/network/udp_socket.cpp b/src/network/udp_socket.cpp new file mode 100644 index 0000000..3734590 --- /dev/null +++ b/src/network/udp_socket.cpp @@ -0,0 +1,104 @@ + +#include "network.h" +#include "log.h" +#include + +UDPSocket::UDPSocket() +{ + handle = -1; +} + +bool UDPSocket::Open(uint16_t port) +{ + //create + handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if(handle <= 0){ + LOG("Failed to create socket (%d)\n", handle); + return false; + } + + //bind port + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + int result = bind(handle, (sockaddr*)&address, sizeof(address)); + if(result < 0){ + LOG("Failed to bind socket (%d %d)\n", handle, result); + return false; + } + + //set non blocking +#ifdef WIN32 + DWORD nonBlocking = 1; + if(ioctlsocket(handle, FIONBIO, &nonBlocking) != 0){ + LOG("Failed to set non-blocking socket (%d)\n", handle); + return false; + } +#else + int nonBlocking = 1; + if(fcntl(handle, F_SETFL, O_NONBLOCK, nonBlocking) == -1){ + LOG("Failed to set non-blocking socket (%d)\n", handle); + return false; + } +#endif + return true; +} + +bool UDPSocket::isOpen() +{ + return (handle != -1); +} + +void UDPSocket::Close() +{ +#ifdef WIN32 + closesocket(handle); +#else + close(handle); +#endif + handle = -1; +} + +bool UDPSocket::Send(const IPAddress &address, const char *data, int len) +{ + if(handle == -1){ + LOG("No socket\n"); + return false; + } + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(address.ip); + addr.sin_port = htons(address.port); + + int send_bytes = sendto(handle, data, len, 0, (sockaddr*)&addr, sizeof(addr)); + + if(send_bytes != len){ + LOG("Failed to send packet to (%d.%d.%d.%d:%d): return value = %d\n", address.Get(0), address.Get(1), address.Get(2), address.Get(3), address.port, send_bytes); + return false; + } + + return true; +} + +int UDPSocket::Receive(IPAddress &from, char *data, int len) +{ +#ifdef WIN32 + typedef int socklen_t; +#endif + sockaddr_in addr_from; + socklen_t addr_size = sizeof(addr_from); + + int received_bytes = recvfrom(handle, data, len, 0, (sockaddr*)&addr_from, &addr_size); + + if(received_bytes <= 0) + return 0; + + from.ip = ntohl(addr_from.sin_addr.s_addr); + from.port = ntohs(addr_from.sin_port); + + return received_bytes; +} diff --git a/src/network/udp_socket.h b/src/network/udp_socket.h new file mode 100644 index 0000000..ab47c2c --- /dev/null +++ b/src/network/udp_socket.h @@ -0,0 +1,17 @@ + +#pragma once + +#include + +class UDPSocket +{ + int handle; +public: + UDPSocket(); + + bool Open(uint16_t port); + bool isOpen(); + void Close(); + bool Send(const IPAddress &address, const char *data, int len); + int Receive(IPAddress &from, char *data, int len); +}; diff --git a/src/renderer/BokehBlur.cpp b/src/renderer/BokehBlur.cpp new file mode 100644 index 0000000..1accb1e --- /dev/null +++ b/src/renderer/BokehBlur.cpp @@ -0,0 +1,120 @@ + +#include +#include "BokehBlur.h" +#include "graphics/glsl_prog.h" +#include "graphics/ArrayBuffer.h" +#include "graphics/fbo.h" + +#define min(x,y) (xCreateFromFile("bokeh","bokeh"); + u_size = progBokeh->GetUniformLoc("u_size"); + int u_coreTex = progBokeh->GetUniformLoc("u_coreTex"); + progBokeh->Use(); + glUniform1i(u_coreTex,1); + glUseProgram(0); + + gridW=w; + gridH=h; +#if USHORT_VERTS + GLushort *gridVerts = new GLushort[w*h*2]; + float stepx = 65536/(float)w; + float stepy = 65536/(float)h; + for(int i=0;iCreate(); + vboGrid->Upload(w*h*2*2,gridVerts); + delete[] gridVerts; +#else + float *gridVerts = new float[w*h*2]; + float stepx = 1.0f/(float)w; + float stepy = 1.0f/(float)h; + for(int i=0;iCreate(); + vboGrid->Upload(w*h*2*4,gridVerts); + delete[] gridVerts; +#endif + + GLubyte *coreTexData = new GLubyte[32*32]; + for(int i=0;i<32*32;i++){ + float x = i%32-15.5f; + float y = i/32-15.5f; + coreTexData[i]=0; + float l = sqrt(x*x+y*y); + if(l<15.0f){ + coreTexData[i]=127*min((15.0f-l)*0.5,1.0f); + } + } + texCore=new Texture(); + texCore->Create(32,32); + texCore->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + texCore->Upload(0,GL_LUMINANCE,GL_LUMINANCE,GL_UNSIGNED_BYTE,coreTexData); + glGenerateMipmap(texCore->target); + delete[] coreTexData; + glBindTexture(GL_TEXTURE_2D,0); + + texSrc=src; + + return true; +} + +void BokehBlur::Render(float size, Texture *src){ + progBokeh->Use(); + glUniform1f(u_size,size); + + glActiveTexture(GL_TEXTURE1); + texCore->Bind(); + glActiveTexture(GL_TEXTURE0); + if(src) + src->Bind(); + else + texSrc->Bind(); + + glEnable(GL_BLEND); + glBlendFunc(1,1); + vboGrid->Bind(); +#if USHORT_VERTS + glVertexAttribPointer(0,2,GL_UNSIGNED_SHORT,GL_TRUE,4,0); +#else + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,8,0); +#endif + glDrawArrays(GL_POINTS,0,gridW*gridH); + vboGrid->Unbind(); + glDisable(GL_BLEND); +} + diff --git a/src/renderer/BokehBlur.h b/src/renderer/BokehBlur.h new file mode 100644 index 0000000..24623da --- /dev/null +++ b/src/renderer/BokehBlur.h @@ -0,0 +1,24 @@ + +#pragma once + +class glslProg; +class VertexBufferObject; +class Texture; + +class BokehBlur{ +public: + BokehBlur(); + ~BokehBlur(); + + bool Init(int w, int h, Texture *src); + void Render(float size, Texture *src=0); + + glslProg *progBokeh; + int u_size; + VertexBufferObject *vboGrid; + int gridW; + int gridH; + Texture *texCore; + Texture *texSrc; +}; + diff --git a/src/renderer/GLES2Renderer.h b/src/renderer/GLES2Renderer.h new file mode 100644 index 0000000..2ef2448 --- /dev/null +++ b/src/renderer/GLES2Renderer.h @@ -0,0 +1,73 @@ +#pragma once + +#include "graphics/ArrayBuffer.h" +#include "graphics/glsl_prog.h" +#include "graphics/texture.h" +class Font; +#include "graphics/fbo.h" +class Mesh; + +enum eDrawState{ + eDSDefault, + eDSDepth +}; + +class GLES2Renderer: public IRenderer +{ +public: + GLES2Renderer(); + virtual void Init(int initFlags,ResourceManager *resMan); + virtual void Resize(int w, int h); + virtual void Draw(); + + virtual void Clear(); + virtual void DrawText(const char *t,float x,float y,float s); + virtual void DrawRect(float x, float y, float w, float h); + virtual void DrawModel(Model *mdl,const glm::mat4 &mtx); + + virtual void SetScene(Scene *sc); + virtual void SetCamera(Camera *cam); + virtual void SetModelMtx(const glm::mat4 &mtx); + void UpdateMtx(); + virtual void UseProg(glslProg *p); + virtual void Set2DMode(bool m = true); + virtual void ResetViewport(); + virtual void SetColor(float r, float g, float b, float a); + virtual void AddButton(Button *b); + virtual void SetBackBufferScale(float s); + virtual FrameBufferObject *GetFBO(); + virtual void RenderDepth(Camera *cam); + virtual void DrawBBox(BoundingBox *bbox); + virtual void DrawCube(); + + VertexBufferObject vboQuad; + glslProg progTex; + Texture texWhite; + Font *font; + + glslProg progDepth; + + FrameBufferObject backFBO; + float backBufferScale; + + glslProg *progCur; + + void DrawSkyBox(Texture *s); + glslProg progSkybox; + Mesh *meshCube; + + ILighting *lighting; + + eDrawState drawState; + + //debug + void DrawDebugScene(); + struct debugBBox_t{ + BoundingBox *bbox; + mat4 mtx; + + debugBBox_t(BoundingBox *b,mat4 m):bbox(b),mtx(m){} + }; + std::vector debugBBox; +}; + diff --git a/src/renderer/LightingForward.cpp b/src/renderer/LightingForward.cpp new file mode 100644 index 0000000..94836a7 --- /dev/null +++ b/src/renderer/LightingForward.cpp @@ -0,0 +1,83 @@ + +#include "renderer/renderer.h" +#include "graphics/platform_gl.h" +#include "graphics/glsl_prog.h" +#include "renderer/Model.h" + +using glm::vec3; + +class LightingForward: public ILighting{ +public: + LightingForward(){ + r = 0; + camera = 0; + } + + virtual void Init(IRenderer *rend){ + r = rend; + + progLightDir.CreateFromFile("generic","lightDir"); + progLightDir.u_mvpMtx = progLightDir.GetUniformLoc("u_mvpMtx"); + progLightDir.u_modelMtx = progLightDir.GetUniformLoc("u_modelMtx"); + progLightDir.u_cameraPos = progLightDir.GetUniformLoc("u_camPos"); + u_lightDir = progLightDir.GetUniformLoc("u_lightDir"); + + progLightPoint.CreateFromFile("lightPoint","lightPoint"); + progLightPoint.u_mvpMtx = progLightPoint.GetUniformLoc("u_mvpMtx"); + progLightPoint.u_modelMtx = progLightPoint.GetUniformLoc("u_modelMtx"); + progLightPoint.u_cameraPos = progLightPoint.GetUniformLoc("u_camPos"); + u_lightPos = progLightPoint.GetUniformLoc("u_lightPos"); + u_lightColor = progLightPoint.GetUniformLoc("u_lightColor"); + u_lightSize = progLightPoint.GetUniformLoc("u_lightSize"); + } + virtual void Draw(){ + + } + virtual void SetDirectional() + { + r->UseProg(&progLightDir); + glUniform3fv(progLightDir.u_cameraPos,1,&(camera->pos.x)); + glUniform3fv(u_lightDir,1,&(scene->sunDirection.x)); + } + virtual void SetCamera(Camera *cam){ + camera=cam; + } + virtual void SetScene(Scene *s){ + scene = s; + } + virtual void AddLights(const mat4 &mtx, const submesh_t &submesh){ + glDepthFunc(GL_EQUAL); + glEnable(GL_BLEND); + glBlendFunc(1,1); + r->UseProg(&progLightPoint); + glUniform3fv(progLightPoint.u_cameraPos,1,&(camera->pos.x)); + vec3 modelPos = vec3(mtx[3]); + for(uint32_t l=0; llights.size();l++){ + LightObject *li=(scene->lights[l]); + float dist = glm::length(li->pos-modelPos); + if(dist > li->radius) + continue; + glUniform3fv(u_lightPos,1,&(li->pos.x)); + glUniform3fv(u_lightColor,1,&(li->color.x)); + glUniform1f(u_lightSize,li->radius); + glDrawArrays(GL_TRIANGLES, submesh.offs, submesh.count); + } + glDepthFunc(GL_LESS); + glDisable(GL_BLEND); + } + + IRenderer *r; + Camera *camera; + Scene *scene; + glslProg progLightDir; + int u_lightDir; + glslProg progLightPoint; + int u_lightPos; + int u_lightColor; + int u_lightSize; +}; + +ILighting *GetLighting(){ + return new LightingForward(); +} + diff --git a/src/renderer/Material.h b/src/renderer/Material.h new file mode 100644 index 0000000..fa22d45 --- /dev/null +++ b/src/renderer/Material.h @@ -0,0 +1,20 @@ + +#pragma once + +class IRenderer; +class Texture; + +class IMaterial{ +public: + virtual void Bind(IRenderer *r)=0; + bool lit;//TODO flags? +}; + +//in renderer.cpp +class TexMaterial: public IMaterial{ +public: + TexMaterial(Texture *t,bool light=false):tex(t){lit=light;} + virtual void Bind(IRenderer *r); + Texture *tex; +}; + diff --git a/src/renderer/Model.cpp b/src/renderer/Model.cpp new file mode 100644 index 0000000..2093aff --- /dev/null +++ b/src/renderer/Model.cpp @@ -0,0 +1,73 @@ + +#include +#include "Model.h" + +Model::Model(): vertexFormat(),submeshes(),materials(),colliders(),animations() +{ + verts = 0; + inds = 0; + + vertexCount = 0; + indexCount = 0; +} + +Model::~Model() +{ + +} + +void Model::Free() +{ + if(verts) + delete[] verts; + if(inds) + delete[] inds; +} + +uint32_t Model::GetIndex(int i) +{ + if(indexSize==4) + return ((uint32_t*)inds)[i]; + else if(indexSize==2) + return ((uint16_t*)inds)[i]; + return 0; +} + +glm::vec3 Model::GetPos(int i) +{ + vertAttrib_t &va = vertexFormat[0]; + char *v = verts+va.offset+va.stride*i; + + if(va.type==GL_FLOAT&&va.size==3){ + return *(vec3*)v; + } + return vec3(0); +} + +glm::vec3 Model::GetNorm(int i) +{ + vertAttrib_t &va = vertexFormat[1]; + char *v = verts+va.offset+va.stride*i; + + if(va.type==GL_FLOAT&&va.size==3) + return *(vec3*)v; + if(va.type==GL_BYTE&&va.norm){ + glm::tvec3 bvec = *(glm::tvec3*)v; + vec3 r=bvec; + return r/127.0f; + } + return vec3(0); +} + +glm::vec2 Model::GetUV(int i) +{ + vertAttrib_t &va = vertexFormat[2]; + char *v = verts+va.offset+va.stride*i; + + if(va.type==GL_FLOAT&&va.size==2) + return *(vec2*)v; + if(va.type==GL_HALF_FLOAT&&va.size==2){ + return vec2(glm::unpackHalf1x16(*(short*)v),glm::unpackHalf1x16(*(short*)(v+2))); + } + return vec2(0); +} diff --git a/src/renderer/Model.h b/src/renderer/Model.h new file mode 100644 index 0000000..e616c9c --- /dev/null +++ b/src/renderer/Model.h @@ -0,0 +1,145 @@ + +#pragma once + +#include +#include +#include "system/neArray.h" +#include "graphics/ArrayBuffer.h" +#include "renderer/Material.h" +#include "cull/BoundingBox.h" + +using glm::vec2; +using glm::vec3; + +class Texture; + +struct submesh_t{ + uint32_t offs; + int count; + int mat; +}; + +struct bone_t{ + int parent; + glm::vec3 pos; + //vec3 rot; + glm::quat rot; +}; + +struct AnimFrame_t{ + AnimFrame_t():bones(){} + neArray bones; +}; + +struct Animation_t{ + Animation_t():name(),frames(){} + std::string name; + float fps; + neArray frames; +}; + +enum eVertType{ + eVertType_byte=0, + eVertType_bvec2, + eVertType_bvec3, + eVertType_bvec4, + eVertType_nbyte=4,//normalized + eVertType_nbvec2, + eVertType_nbvec3, + eVertType_nbvec4, + eVertType_ubyte=8,//unsigned + eVertType_ubvec2, + eVertType_ubvec3, + eVertType_ubvec4, + eVertType_nubyte=12,//normalized unsigned + eVertType_nubvec2, + eVertType_nubvec3, + eVertType_nubvec4, + + eVertType_short=16, + eVertType_svec2, + eVertType_svec3, + eVertType_svec4, + eVertType_nshort=20,//normalized + eVertType_nsvec2, + eVertType_nsvec3, + eVertType_nsvec4, + eVertType_ushort=24,//unsigned + eVertType_usvec2, + eVertType_usvec3, + eVertType_usvec4, + eVertType_nushort=28,//normalized unsigned + eVertType_nusvec2, + eVertType_nusvec3, + eVertType_nusvec4, + + eVertType_float=32, + eVertType_vec2, + eVertType_vec3, + eVertType_vec4, + + eVertType_hfloat=48,//half float + eVertType_hvec2, + eVertType_hvec3, + eVertType_hvec4 +}; + +struct vertAttrib_t{ + vertAttrib_t(){} + vertAttrib_t(int i, int s, int t, bool n, int st, uint64_t o): + id(i),size(s),type(t),norm(n),stride(st),offset(o){} + int id; + int size; + int type; + bool norm; + int stride; + uint64_t offset; +}; + +//Temp +struct nmfMaterial{ + nmfMaterial():mat(0){} + std::string name; + IMaterial *mat; +}; + +#define NMF_COLLIDER_BOX 1 + +struct nmfCollider_t{ + uint16_t type; + uint16_t flags; + vec3 pos; + vec3 rot; + vec3 size; +}; + +class Model +{ +public: + Model(); + ~Model(); + void Free(); + + BoundingBox bbox; + int vertexCount; + //int vertexStride; + neArray vertexFormat; + char *verts; + VertexBufferObject vbo; + int indexCount; + int indexSize; + int indexType; + char *inds; + IndexBufferObject ibo; + + neArray submeshes; + neArray materials; + neArray colliders; + neArray skeleton; + neArray animations; + + uint32_t GetIndex(int i); + vec3 GetPos(int i); + vec3 GetNorm(int i); + vec2 GetUV(int i); +}; diff --git a/src/renderer/camera.cpp b/src/renderer/camera.cpp new file mode 100644 index 0000000..6c3569f --- /dev/null +++ b/src/renderer/camera.cpp @@ -0,0 +1,39 @@ + +#include "renderer/camera.h" + +Camera::Camera() +{ + pos = glm::vec3(0); + rot = glm::vec3(0); + projMtx = glm::mat4(1); + viewMtx = glm::mat4(1); +} + +void Camera::UpdateView() +{ + viewMtx = glm::mat4(1.0f); + viewMtx = glm::rotate( viewMtx, glm::radians(rot.x), glm::vec3(1.0f, 0.0f, 0.0f)); + viewMtx = glm::rotate( viewMtx, glm::radians(rot.y), glm::vec3(0.0f, -1.0f, 0.0f)); + viewMtx = glm::translate( viewMtx, -pos); +} + +void Camera::UpdateProj(float fov, float aspect, float near, float far) +{ + projMtx = glm::perspective(glm::radians(fov), aspect, near, far); +} + +void Camera::SetOrtho(float aspect, float size, float depth) +{ + //projMtx = glm::ortho(-size*aspect, size*aspect, -size, size, size, -size); + projMtx = glm::ortho(size*aspect, -size*aspect, -size, size, depth, -depth); +} + +void Camera::UpdateFrustum() +{ + frustum.Init(projMtx*viewMtx); +} + +void Camera::LookAt(glm::vec3 orig, glm::vec3 to, glm::vec3 up) +{ + viewMtx = glm::lookAt(orig,to,up); +} diff --git a/src/renderer/camera.h b/src/renderer/camera.h new file mode 100644 index 0000000..8d6d82b --- /dev/null +++ b/src/renderer/camera.h @@ -0,0 +1,22 @@ + +#pragma once + +#include +#include "cull/frustum.h" + +class Camera +{ +public: + glm::vec3 pos; + glm::vec3 rot; + glm::mat4 projMtx; + glm::mat4 viewMtx; + Frustum frustum; + + Camera(); + void UpdateView(); + void UpdateProj(float fov, float aspect, float near, float far); + void SetOrtho(float aspect, float size, float depth); + void UpdateFrustum(); + void LookAt(glm::vec3 orig, glm::vec3 to, glm::vec3 up); +}; diff --git a/src/renderer/font.cpp b/src/renderer/font.cpp new file mode 100644 index 0000000..0da8ee2 --- /dev/null +++ b/src/renderer/font.cpp @@ -0,0 +1,330 @@ + +#include +#include +#include +#include +using namespace std; + +#include "log.h" +#include "system/FileSystem.h" + +#include "graphics/platform_gl.h" +#include "graphics/texture.h" +#include "renderer/font.h" +#include "resource/ResourceManager.h" + +struct bmfontInfoBlock_t +{ + uint16_t fontSize; + uint8_t bitField;//bit 0: smooth, bit 1: unicode, bit 2: italic, bit 3: bold, bit 4: fixedHeigth, bits 5-7: reserved + uint8_t charSet; + uint16_t stretchH; + uint8_t aa; + uint8_t paddingUp; + uint8_t paddingRight; + uint8_t paddingDown; + uint8_t paddingLeft; + uint8_t spacingHoriz; + uint8_t spacingVert; + uint8_t outline;//added with version 2 + //fontName n+1 string 14 null terminated string with length n +}; + +struct bmfontCommonBlock_t +{ + uint16_t lineHeight; + uint16_t base; + uint16_t scaleW; + uint16_t scaleH; + uint16_t pages; + uint8_t bitField;//bits 0-6: reserved, bit 7: packed + uint8_t alphaChnl; + uint8_t redChnl; + uint8_t greenChnl; + uint8_t blueChn; + //padding +}; + +struct bmfontChar_t +{ + uint32_t id;//These fields are repeated until all characters have been described + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; + int16_t xoffset; + int16_t yoffset; + int16_t xadvance; + uint8_t page; + uint8_t chnl; +}; + +bool Font::LoadBMFont(const char *fileName,ResourceManager *resMan) +{ + string filePath = "fonts/"+string(fileName)+".fnt"; + IFile *file = g_fs.Open(filePath.c_str()); + if(!file){ + return false; + } + + char ident[4]; + file->Read(ident, 4); + + if(ident[0]!='B'||ident[1]!='M'||ident[2]!='F'){ + Log("LoadBMFont: unknown format\n"); + g_fs.Close(file); + return false; + } + //Log("BMFont \"%s\" version %d\n", fileName, ident[3]); + + char blockType = 0; + int blockSize = 0; + //info block + file->Read(&blockType,1); + if(blockType==1){ + file->Read(&blockSize, 4); + char *block = new char[blockSize]; + file->Read(block, blockSize); + //bmfontInfoBlock_t *infoBlock = (bmfontInfoBlock_t *)block; + //LOG("font size %d, charset %d\n",infoBlock->fontSize,infoBlock->charSet); + //LOG("name %s\n",block+14); + + delete[] block; + }else{ + Log("first block type is not 1\n"); + g_fs.Close(file); + return false; + } + + //common block + //int numPages=0; + int texW=1; + int texH=1; + file->Read(&blockType,1); + if(blockType==2){ + file->Read(&blockSize, 4); + char *block = new char[blockSize]; + file->Read(block, blockSize); + bmfontCommonBlock_t *commonBlock = (bmfontCommonBlock_t *)block; + //LOG("line height %d, base %d, scale %d %d, pages %d\n",commonBlock->lineHeight,commonBlock->base,commonBlock->scaleW,commonBlock->scaleH,commonBlock->pages); + //numPages = commonBlock->pages; + texW = commonBlock->scaleW; + texH = commonBlock->scaleH; + base = (float)commonBlock->base/texW; + delete[] block; + } + + //pages block + //TODO: multiple pages + string pageName; + file->Read(&blockType,1); + if(blockType==3){ + file->Read(&blockSize, 4); + char *block = new char[blockSize]; + file->Read(block, blockSize); + //LOG("page 0: %s\n",block); + pageName = block; + delete[] block; + } + + tex = resMan->GetTexture(("fonts/"+pageName).c_str()); + + //chars block + int numChars = 0; + file->Read(&blockType,1); + if(blockType==4){ + file->Read(&blockSize, 4); + char *block = new char[blockSize]; + file->Read(block, blockSize); + numChars = blockSize/20;//sizeof char + //LOG("numChars %d\n",numChars); + bmfontChar_t *curChar = (bmfontChar_t *)block; + for(int i=0; iid>255){ + Log("LoadBMFont: char %d id %d too big (%s)\n",i,curChar->id,fileName); + continue; + } + chars[curChar->id].x = (float)curChar->x/texW; + chars[curChar->id].y = (float)curChar->y/texH; + chars[curChar->id].width = (float)curChar->width/texW; + chars[curChar->id].height = (float)curChar->height/texH; + chars[curChar->id].xoffset = (float)curChar->xoffset/texW; + chars[curChar->id].yoffset = (float)curChar->yoffset/texH; + chars[curChar->id].xAdv = (float)curChar->xadvance/texW; + curChar++; + } + + delete[] block; + } + + + bmfont = true; + g_fs.Close(file); + return true; +} + +void Font::Print(const char *text, float x, float y, float size) +{ + if(bmfont) + PrintBMFont(text,x,y,size); + else + PrintOLD(text,x,y,size); +} + +void Font::PrintBMFont(const char *text, float x, float y, float size) +{ + if(!tex) + return; + int l = strlen(text); + if(!l) + return; + GLfloat *verts = new GLfloat[l*6*4]; + int cv = 0; + float cx = x; + float cy = y; + + for(int i=0; iBind(); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, verts); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 16, verts+2); + glDrawArrays(GL_TRIANGLES, 0, cv); + + delete[] verts; +} + +void Font::PrintOLD(const char *text, float x, float y, float size) +{ +// static bool first = true; + + if(!tex) + return; + int l = strlen(text); + GLfloat *verts=new GLfloat[l*6*4]; + int cv=0; + float cx=x; + float cy=y; + size *= 0.125; + + for(int i=0; i=' '&&c<='?') + { + c -= ' '; + + } + else if(c>='a'&&c<='x') + { + c -= 'a'; + c += 32; + } + else + c = -1; + if(c>=0) + { + float tx = (c%8)/8.0f; + float tw = 1.0f/8.0f; + float ty = (c/8)/7.0f; + float th = 1.0f/7.0f; + verts[cv*4]=cx; + verts[cv*4+1]=cy; + verts[cv*4+2]=tx; + verts[cv*4+3]=ty+th; + cv++; + verts[cv*4]=cx; + verts[cv*4+1]=cy+size; + verts[cv*4+2]=tx; + verts[cv*4+3]=ty; + cv++; + verts[cv*4]=cx+size; + verts[cv*4+1]=cy; + verts[cv*4+2]=tx+tw; + verts[cv*4+3]=ty+th; + cv++; + + verts[cv*4]=cx+size; + verts[cv*4+1]=cy; + verts[cv*4+2]=tx+tw; + verts[cv*4+3]=ty+th; + cv++; + verts[cv*4]=cx; + verts[cv*4+1]=cy+size; + verts[cv*4+2]=tx; + verts[cv*4+3]=ty; + cv++; + verts[cv*4]=cx+size; + verts[cv*4+1]=cy+size; + verts[cv*4+2]=tx+tw; + verts[cv*4+3]=ty; + cv++; + } + cx+=size*13.0f/12.0f; + } + tex->Bind(); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, verts); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 16, verts+2); + glDrawArrays(GL_TRIANGLES, 0, cv); + + delete[] verts; + //first=false; +} diff --git a/src/renderer/font.h b/src/renderer/font.h new file mode 100644 index 0000000..a460d53 --- /dev/null +++ b/src/renderer/font.h @@ -0,0 +1,34 @@ + +#pragma once + +class ResourceManager; +class Texture; + +struct fontChar_t +{ + float x; + float y; + float width; + float height; + float xoffset; + float yoffset; + float xAdv; +}; + +class Font +{ +public: + Texture *tex; + bool bmfont; + fontChar_t chars[256]; + Font():tex(0),bmfont(0),base(0){} + Font(Texture *t):tex(t),bmfont(0),base(0){} + bool LoadBMFont(const char *fileName,ResourceManager *resMan); + void Print(const char *text,float x, float y, float size); +private: + void PrintBMFont(const char *text,float x, float y, float size); + void PrintOLD(const char *text,float x, float y, float size); + + float base; +}; + diff --git a/src/renderer/mesh.cpp b/src/renderer/mesh.cpp new file mode 100644 index 0000000..a73d658 --- /dev/null +++ b/src/renderer/mesh.cpp @@ -0,0 +1,95 @@ + +#include "graphics/platform_gl.h" +#include "graphics/ArrayBuffer.h" +#include "mesh.h" + +void Mesh::Bind() +{ + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, verts); +} + +void Mesh::Draw() +{ + glDrawArrays(mode, 0, numVerts); +} + +void Mesh::Free() +{ + if(verts) + delete[] verts; + verts = 0; + numVerts = 0; + if(inds) + delete[] inds; + inds = 0; + numInds = 0; +} + +MeshFBO::MeshFBO(float *v, int nv, int m): Mesh(v, nv, m) +{ + vbo.Create(); + vbo.Upload(sizeof(float)*3*numVerts, verts); +} + +void MeshFBO::Bind() +{ + vbo.Bind(); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0); +} + +void MeshFBO::Unbind() +{ + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void Mesh_N3_T2::Bind() +{ + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 32, verts); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 32, verts+3); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 32, verts+6); +} + +void Mesh_N3_T2::DrawPos() +{ + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 32, verts); + glDrawArrays(mode, 0, numVerts); +} + +MeshFBO_N3_T2::MeshFBO_N3_T2(float *v, int nv, int m) +{ + verts = v; + numVerts = nv; + inds = 0; + numInds = 0; + mode = m; + vbo.Create(); + vbo.Upload(sizeof(float)*8*numVerts, verts); +} + +MeshFBO_N3_T2::MeshFBO_N3_T2(float *v, int nv, GLushort *i, int ni, int m) +{ + verts = v; + numVerts = nv; + inds = i; + numInds = ni; + mode = m; + vbo.Create(); + vbo.Upload(sizeof(float)*8*numVerts, verts); +} + +void MeshFBO_N3_T2::Bind() +{ + vbo.Bind(); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 32, 0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 32, (void*)12); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 32, (void*)24); +} + +void MeshFBO_N3_T2::DrawPos() +{ + vbo.Bind(); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 32, 0); + glDrawArrays(mode, 0, numVerts); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + diff --git a/src/renderer/mesh.h b/src/renderer/mesh.h new file mode 100644 index 0000000..30e5f72 --- /dev/null +++ b/src/renderer/mesh.h @@ -0,0 +1,70 @@ + +#pragma once + +#include "graphics/ArrayBuffer.h" + +//TODO implement extension GL_OES_element_index_uint +class Mesh +{ +public: + GLfloat *verts; + GLuint numVerts; + GLushort *inds; + GLuint numInds; + GLuint mode; + + Mesh(): verts(0), numVerts(0), inds(0), numInds(0), mode(GL_TRIANGLES) + {} + Mesh(float *v, int nv, int m): verts(v), numVerts(nv), inds(0), numInds(0), mode(m) + {} + Mesh(float *v, int nv, GLushort *i, int ni, int m): verts(v), numVerts(nv), inds(i), numInds(ni), mode(m) + {} + + virtual ~Mesh(){} + + virtual void Bind(); + virtual void Draw(); + virtual void Unbind() + {} + void Free(); +}; + +class MeshFBO: public Mesh +{ +public: + VertexBufferObject vbo; + MeshFBO(): Mesh() + {} + MeshFBO(float *v, int nv, int m); + virtual void Bind(); + //void Draw(); + void Unbind(); +}; + +class Mesh_N3_T2: public Mesh +{ +public: + Mesh_N3_T2(float *v, int nv, int m):Mesh(v, nv, m) + {} + Mesh_N3_T2(float *v, int nv, GLushort *i, int ni, int m):Mesh(v, nv, i, ni, m) + {} + void Bind(); + //void Draw(); + //void Unbind(); + void DrawPos(); +}; + +class MeshFBO_N3_T2: public MeshFBO +{ +public: + MeshFBO_N3_T2(float *v, int nv, int m); + MeshFBO_N3_T2(float *v, int nv, GLushort *i, int ni, int m); + void Bind(); + //void Draw(); + //void Unbind(); + void DrawPos(); +}; + +Mesh* LoadMeshFile(const char* fileName, bool vbo); +Mesh* LoadObjFile(const char* fileName, bool vbo); + diff --git a/src/renderer/oldFont.txt b/src/renderer/oldFont.txt new file mode 100644 index 0000000..d83b197 --- /dev/null +++ b/src/renderer/oldFont.txt @@ -0,0 +1,53 @@ + +#define w 255 + GLubyte ftData[] = + { + //32 + 0,0,0,0,0, 0,0,w,0,0, 0,w,0,w,0, 0,w,0,w,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,w,0,0, + 0,0,0,0,0, 0,0,w,0,0, 0,w,0,w,0, w,w,w,w,w, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,w,0,0, + 0,0,0,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,w,0,w,0, 0,0,w,0,0, 0,0,w,0,0, 0,0,w,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, w,w,w,w,w, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,w,0,w,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + //40 + 0,0,0,w,0, 0,w,0,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,w,0, + 0,0,w,0,0, 0,0,w,0,0, 0,w,w,w,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,w,0, + 0,0,w,0,0, 0,0,w,0,0, 0,w,w,0,0, 0,w,w,w,0, 0,0,0,0,0, 0,w,w,w,0, 0,0,0,0,0, 0,0,w,0,0, + 0,0,w,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,w,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,w,0,0,0, + 0,0,0,w,0, 0,w,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,w,0,0, 0,w,0,0,0, + //48 + 0,w,w,0,0, 0,0,w,0,0, 0,w,w,0,0, 0,w,w,0,0, w,0,0,w,0, w,w,w,w,0, 0,w,w,0,0, w,w,w,w,0, + w,0,0,w,0, 0,w,w,0,0, w,0,0,w,0, w,0,0,w,0, w,0,0,w,0, w,0,0,0,0, w,0,0,0,0, 0,0,0,w,0, + w,0,0,w,0, 0,0,w,0,0, 0,0,w,0,0, 0,0,w,0,0, w,w,w,w,0, w,w,w,0,0, w,w,w,0,0, 0,0,w,0,0, + w,0,0,w,0, 0,0,w,0,0, 0,w,0,0,0, w,0,0,w,0, 0,0,0,w,0, 0,0,0,w,0, w,0,0,w,0, 0,w,0,0,0, + 0,w,w,0,0, 0,w,w,w,0, w,w,w,w,0, 0,w,w,0,0, 0,0,0,w,0, w,w,w,0,0, 0,w,w,0,0, 0,w,0,0,0, + //56 + 0,w,w,0,0, 0,w,w,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,w,0, 0,0,0,0,0, 0,w,0,0,0, 0,w,w,0,0, + w,0,0,w,0, w,0,0,w,0, 0,0,w,0,0, 0,0,w,0,0, 0,0,w,0,0, 0,w,w,w,0, 0,0,w,0,0, w,0,0,w,0, + 0,w,w,0,0, 0,w,w,w,0, 0,0,0,0,0, 0,0,0,0,0, 0,w,0,0,0, 0,0,0,0,0, 0,0,0,w,0, 0,0,w,0,0, + w,0,0,w,0, 0,0,0,w,0, 0,0,w,0,0, 0,0,w,0,0, 0,0,w,0,0, 0,w,w,w,0, 0,0,w,0,0, 0,0,0,0,0, + 0,w,w,0,0, 0,w,w,0,0, 0,0,0,0,0, 0,0,w,0,0, 0,0,0,w,0, 0,0,0,0,0, 0,w,0,0,0, 0,0,w,0,0, + //97 + 0,0,0,0,0, w,0,0,0,0, 0,0,0,0,0, 0,0,0,w,0, 0,w,w,0,0, 0,w,w,0,0, 0,w,w,0,0, w,0,0,0,0, + 0,w,w,w,0, w,w,w,0,0, 0,w,w,0,0, 0,w,w,w,0, w,0,0,w,0, 0,w,0,0,0, w,0,0,0,0, w,w,w,0,0, + w,0,0,w,0, w,0,0,w,0, w,0,0,0,0, w,0,0,w,0, w,w,w,w,0, 0,w,w,0,0, w,0,w,w,0, w,0,0,w,0, + w,0,0,w,0, w,0,0,w,0, w,0,0,0,0, w,0,0,w,0, w,0,0,0,0, 0,w,0,0,0, w,0,0,w,0, w,0,0,w,0, + 0,w,w,w,0, w,w,w,0,0, 0,w,w,0,0, 0,w,w,w,0, 0,w,w,0,0, 0,w,0,0,0, 0,w,w,0,0, w,0,0,w,0, + + 0,0,w,0,0, 0,0,w,0,0, 0,w,0,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0,w,0,w,0, 0,0,w,0,0, w,w,0,w,0, w,w,w,0,0, 0,w,w,0,0, w,w,w,0,0, + 0,0,w,0,0, 0,0,w,0,0, 0,w,w,0,0, 0,0,w,0,0, w,0,w,0,w, w,0,0,w,0, w,0,0,w,0, w,0,0,w,0, + 0,0,w,0,0, 0,0,w,0,0, 0,w,0,w,0, 0,0,w,0,0, w,0,w,0,w, w,0,0,w,0, w,0,0,w,0, w,w,w,0,0, + 0,0,w,0,0, 0,w,0,0,0, 0,w,0,w,0, 0,0,w,0,0, w,0,w,0,w, w,0,0,w,0, 0,w,w,0,0, w,0,0,0,0, + + 0,0,0,0,0, 0,0,0,0,0, 0,w,w,0,0, 0,0,w,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,w,w,w,0, w,0,w,w,0, w,0,0,0,0, 0,w,w,w,0, w,0,0,w,0, w,0,0,0,w, w,0,w,0,w, w,0,0,w,0, + w,0,0,w,0, w,w,0,0,0, 0,w,w,0,0, 0,0,w,0,0, w,0,0,w,0, 0,w,0,w,0, w,0,w,0,w, 0,w,w,0,0, + 0,w,w,w,0, w,0,0,0,0, 0,0,0,w,0, 0,0,w,0,0, w,0,0,w,0, 0,w,0,w,0, w,0,w,0,w, w,0,0,w,0, + 0,0,0,w,0, w,0,0,0,0, 0,w,w,0,0, 0,0,0,w,0, 0,w,w,w,0, 0,0,w,0,0, 0,w,0,w,0, w,0,0,w,0 + }; +#undef w + Texture *ft = new Texture(); + ft->Create(40,35); + ft->Upload(0,GL_LUMINANCE,ftData); + testFont = Font(ft); + diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp new file mode 100644 index 0000000..de8181b --- /dev/null +++ b/src/renderer/renderer.cpp @@ -0,0 +1,545 @@ + +#include +using std::vector; + +#include +#include +#include +using glm::mat4; + +#include "log.h" +#include "renderer/renderer.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/ArrayBuffer.h" +#include "graphics/glsl_prog.h" +#include "graphics/texture.h" +#include "graphics/fbo.h" +#include "renderer/font.h" +#include "renderer/Model.h" +#include "renderer/mesh.h" +#include "button.h" +#include "engine.h" + +#include "renderer/GLES2Renderer.h" + +void LogRenderInfo(); + +IRenderer *CreateRenderer() +{ + return new GLES2Renderer(); +} + +void TexMaterial::Bind(IRenderer *r){ + if(tex) + tex->Bind(); +} + +GLES2Renderer::GLES2Renderer() +{ + font = 0; + lighting = 0; +} + +void GLES2Renderer::Init(int initFlags,ResourceManager *resMan) +{ + flags = initFlags; + Log("Init renderer\n"); + + LogRenderInfo(); + + progTex.CreateFromFile("generic", "col_tex"); + progTex.u_mvpMtx = progTex.GetUniformLoc("u_mvpMtx"); + progTex.u_modelMtx = -1; + progTex.u_color = progTex.GetUniformLoc("u_color"); + progTex.Use(); + glUniform4f(progTex.u_color,1,1,1,1); + + progDepth.CreateFromFile("generic","null"); + progDepth.u_mvpMtx = progDepth.GetUniformLoc("u_mvpMtx"); + + progSkybox.CreateFromFile("skybox","skybox"); + progSkybox.u_mvpMtx = progSkybox.GetUniformLoc("u_mvpMtx"); + + meshCube = LoadMeshFile("cube", true); + + GLubyte whiteData[] = {255}; + texWhite.Create(1,1); + texWhite.Upload(0, GL_LUMINANCE, whiteData); + + if(flags&RENDERER_GUI){ + float quadVerts[] ={ + 1,-1,1,0, + 1, 1,1,1, + -1, 1,0,1, + -1,-1,0,0}; + + vboQuad.Create(); + vboQuad.Upload(4*4*4, quadVerts); + + if(resMan){ + font = new Font(); + font->LoadBMFont("sansation",resMan); + } + } + if(flags&RENDERER_LIGHT){ + lighting = GetLighting(); + lighting->Init(this); + } + + if(flags&RENDERER_BACKBUFFER){ + backBufferScale = 1; + backFBO.Create(); + backFBO.CreateTexture(64,64, GL_LINEAR); + //backFBO.AddDepthBuffer(); + backFBO.CreateDepthTexture(64,64); + } + + glClearColor(0,0,0,1); + //glClearColor(0.4f,0.4f,0.4f,1); + glUseProgram(0); + + modelMtx = glm::mat4(1.0f); + vpMtx = glm::mat4(1.0f); + mvpMtx = glm::mat4(1.0f); + + progCur = 0; + + debug = false; + drawState = eDSDefault; + + CheckGLError("GLES2Renderer::Init", __FILE__, __LINE__); +} + +void GLES2Renderer::SetBackBufferScale(float s) +{ + if(!(flags&RENDERER_BACKBUFFER)){ + return; + } + if(s==backBufferScale) + return; + backBufferScale=s; + backFBO.Resize(width*s,height*s); +} + +FrameBufferObject *GLES2Renderer::GetFBO() +{ + if(!(flags&RENDERER_BACKBUFFER)){ + return NULL; + } + return &backFBO; +} + +void GLES2Renderer::Resize(int w, int h) +{ + width = w; + height = h; + aspect = w/(float)h; + if(flags&RENDERER_BACKBUFFER){ + backFBO.Resize(width*backBufferScale,height*backBufferScale); + } + glViewport(0,0,w,h); +} + +void GLES2Renderer::Draw() +{ + glBindFramebuffer(GL_FRAMEBUFFER,0); + Clear(); + + progCur = 0; + UseProg(&progTex); + texWhite.Bind(); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + if(scene){ + if(flags&RENDERER_BACKBUFFER){ + glBindTexture(GL_TEXTURE_2D,0); + backFBO.Bind(); + Clear(); + } + if(scene->skyBox){ + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + DrawSkyBox(scene->skyBox); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + UseProg(&progTex); + texWhite.Bind(); + } + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + UpdateMtx(); + if(debug) + debugBBox.clear(); + scene->Draw(this); + if(debug){ + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + DrawDebugScene(); + glEnableVertexAttribArray(2); + } + if(flags&RENDERER_BACKBUFFER){ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, width, height); + mvpMtx = glm::mat4(1.0f); + UseProg(&progTex); + SetColor(1,1,1,1); + backFBO.BindTexture(); + glDisable(GL_DEPTH_TEST); + vboQuad.Bind(); + glDisableVertexAttribArray(1); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + vboQuad.Unbind(); + } + } + glDisableVertexAttribArray(1); + + //2d + UseProg(&progTex); + texWhite.Bind(); + Set2DMode(); + SetColor(1,1,1,1); + glDisable(GL_CULL_FACE); + for(uint32_t i=0;iactive&&b->text){ + DrawText(b->text,b->x*aspect+0.01,b->y+b->h-0.01,0.5); + } + } + + glEnable(GL_BLEND); + glBlendFunc(1,1); + glUniform4f(progTex.u_color,0.3,0.3,0.3,0.3); + for(uint32_t i=0;iactive&&b->text){ + DrawRect(b->x,b->y,b->w,b->h); + } + } + glUniform4f(progTex.u_color,1,1,1,1); + glDisable(GL_BLEND); + + //glUniformMatrix4fv(progTex.u_mvpMtx,1,false,glm::value_ptr(mtx)); + + CheckGLError("GLES2Renderer::Draw", __FILE__, __LINE__); +} + +void GLES2Renderer::RenderDepth(Camera *cam) +{ + if(!scene){ + return; + } + drawState = eDSDepth; + glEnableVertexAttribArray(0); + UseProg(&progDepth); + glBindTexture(GL_TEXTURE_2D,0); + glEnable(GL_DEPTH_TEST); + + vpMtx = cam->projMtx*cam->viewMtx; + mvpMtx = vpMtx*modelMtx; + glUniformMatrix4fv(progCur->u_mvpMtx,1,false,glm::value_ptr(mvpMtx)); + + scene->Draw(this); + + UseProg(&progTex); + texWhite.Bind(); + drawState = eDSDefault; + CheckGLError("GLES2Renderer::RenderDepth", __FILE__, __LINE__); +} + +void GLES2Renderer::DrawDebugScene() +{ + glDisable(GL_DEPTH_TEST); + UseProg(&progTex); + texWhite.Bind(); + SetColor(1,0.5,0.2,1); + for(uint32_t i=0; ilights.size(); i++){ + LightObject &l=*(scene->lights[i]); + mat4 mtx(1); + SetModelMtx(mtx); + BoundingBox bbox(l.pos-l.radius,l.pos+l.radius); + DrawBBox(&bbox); + bbox = BoundingBox(l.pos-vec3(0.1),l.pos+vec3(0.1)); + DrawBBox(&bbox); + } + + glEnable(GL_DEPTH_TEST); + CheckGLError("GLES2Renderer::DrawDebugScene", __FILE__, __LINE__); +} + +void GLES2Renderer::DrawBBox(BoundingBox *bbox) +{ + float r = bbox->max.x; + float l = bbox->min.x; + float u = bbox->max.y; + float d = bbox->min.y; + float f = bbox->max.z; + float b = bbox->min.z; + + GLfloat vertices[] = + { + r, d, f, + r, d, b, + l, d, b, + l, d, f, + r, u, f, + r, u, b, + l, u, b, + l, u, f + }; + uint8_t inds[] = + { + 0,1, 1,2, 2,3, 3,0, + 4,5, 5,6, 6,7, 7,4, + 0,4, 1,5, 2,6, 3,7 + }; + + //glDisableVertexAttribArray(1); + //glDisableVertexAttribArray(2); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, vertices); + glDrawElements(GL_LINES, 24, GL_UNSIGNED_BYTE, inds); + + CheckGLError("GLES2Renderer::DrawBBox", __FILE__, __LINE__); +} + +void GLES2Renderer::DrawCube() +{ + meshCube->Bind(); + meshCube->Draw(); + meshCube->Unbind(); + CheckGLError("GLES2Renderer::DrawCube", __FILE__, __LINE__); +} + +void GLES2Renderer::Clear() +{ + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); +} + +void GLES2Renderer::DrawModel(Model *mdl,const glm::mat4 &mtx) +{ + //TODO: render list from vbsp + + if(drawState!=eDSDepth && debug) + debugBBox.push_back(debugBBox_t(&mdl->bbox,mtx)); + + SetModelMtx(mtx); + + //TODO: vertexFormat + mdl->vbo.Bind(); + /*glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,mdl->vertexStride,0); + if(drawState!=eDSDepth){ + glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,mdl->vertexStride,(void*)12); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,mdl->vertexStride,(void*)24); + }*/ + vertAttrib_t &va = mdl->vertexFormat[0]; + glVertexAttribPointer(va.id,va.size,va.type,va.norm,va.stride,(void*)va.offset); + if(drawState!=eDSDepth){ + for(int i=1; ivertexFormat.size; i++){ + vertAttrib_t &va = mdl->vertexFormat[i]; + glVertexAttribPointer(va.id,va.size,va.type,va.norm,va.stride,(void*)va.offset); + } + } + mdl->vbo.Unbind(); + + for(int i=0;isubmeshes.size;i++){ + IMaterial *mat = mdl->materials[mdl->submeshes[i].mat].mat; + if(drawState!=eDSDepth && mat){ + mat->Bind(this); + if(mat->lit&&lighting){ + lighting->SetDirectional(); + }else{ + UseProg(&progTex); + } + } + //else + // defaultMat->Bind(this); + if(mdl->indexCount){ + mdl->ibo.Bind(); + glDrawElements(GL_TRIANGLES,mdl->submeshes[i].count,mdl->indexType,(void *)(mdl->submeshes[i].offs*mdl->indexSize)); + mdl->ibo.Unbind(); + }else{ + glDrawArrays(GL_TRIANGLES, mdl->submeshes[i].offs, mdl->submeshes[i].count); + } + + //add lights + if(drawState!=eDSDepth && mat && mat->lit&& lighting&& !scene->lights.empty()){ + lighting->AddLights(mtx,mdl->submeshes[i]); + } + } +} + +void GLES2Renderer::UseProg(glslProg *p){ + progCur = p; + if(!p) + return; + p->Use(); + + glUniformMatrix4fv(progCur->u_mvpMtx,1,false,glm::value_ptr(mvpMtx)); + if(progCur->u_modelMtx!=-1) + glUniformMatrix4fv(progCur->u_modelMtx,1,false,glm::value_ptr(modelMtx)); + if(progCur->u_cameraPos!=-1){ + glUniform3fv(progCur->u_cameraPos, 1, glm::value_ptr(camera->pos)); + } +} + +void GLES2Renderer::SetScene(Scene *sc){ + scene=sc; + if(lighting) + lighting->SetScene(sc); +} + +void GLES2Renderer::SetCamera(Camera *cam) +{ + camera = cam; + if(lighting) + lighting->SetCamera(cam); + vpMtx = cam->projMtx*cam->viewMtx; +} + +void GLES2Renderer::SetModelMtx(const glm::mat4 &mtx) +{ + modelMtx = mtx; + mvpMtx = vpMtx*mtx; + glUniformMatrix4fv(progCur->u_mvpMtx,1,false,glm::value_ptr(mvpMtx)); + if(progCur->u_modelMtx!=-1) + glUniformMatrix4fv(progCur->u_modelMtx,1,false,glm::value_ptr(mtx)); +} + +void GLES2Renderer::UpdateMtx() +{ + if(camera) + vpMtx = camera->projMtx*camera->viewMtx; + mvpMtx = vpMtx*modelMtx; + glUniformMatrix4fv(progCur->u_mvpMtx,1,false,glm::value_ptr(mvpMtx)); + if(progCur->u_modelMtx!=-1) + glUniformMatrix4fv(progCur->u_modelMtx,1,false,glm::value_ptr(modelMtx)); +} + +void GLES2Renderer::ResetViewport(){ + glViewport(0,0,width,height); +} + +void GLES2Renderer::Set2DMode(bool m){ + if(m){ + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + UseProg(&progTex); + glm::mat4 mtx(1.0f); + mtx = glm::translate(mtx,glm::vec3(-1.0,-1.0,0.0)); + vpMtx = glm::scale(mtx,glm::vec3(2.0/aspect,2.0f,1.0f)); + mvpMtx = vpMtx; + glUniformMatrix4fv(progCur->u_mvpMtx,1,false,glm::value_ptr(mvpMtx)); + }else{ + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + UpdateMtx(); + } +} + +void GLES2Renderer::SetColor(float r, float g, float b, float a){ + glUniform4f(progCur->u_color,r,g,b,a); +} + +void GLES2Renderer::AddButton(Button *b) +{ + buttons.push_back(b); +} + +void GLES2Renderer::DrawText(const char *t,float x,float y,float s){ + if(!font) + return; + + UseProg(&progTex); + font->Print(t,x,1-y,s); + texWhite.Bind(); +} + +void GLES2Renderer::DrawRect(float x, float y, float w, float h){ + if(!(flags&RENDERER_GUI)){ + EngineError("Renderer::DrawRect without RENDERER_GUI"); + return; + } + glm::mat4 mtx(1.0f); + mtx = glm::translate(mtx,glm::vec3(x*2.0-(1-w),-(y*2.0-(1-h)),0)); + mtx = glm::scale(mtx,glm::vec3(w,h,1)); + glUniformMatrix4fv(progCur->u_mvpMtx,1,false,glm::value_ptr(mtx)); + + vboQuad.Bind(); + glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,16,0); + glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,16,(void*)8); + glDrawArrays(GL_TRIANGLE_FAN,0,4); + vboQuad.Unbind(); +} + +void GLES2Renderer::DrawSkyBox(Texture *s){ + meshCube->Bind(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glm::mat4 skyboxViewMtx = camera->viewMtx; + skyboxViewMtx[3] = glm::vec4(0,0,0,1); + glm::mat4 mvpMtx = camera->projMtx * skyboxViewMtx; + UseProg(&progSkybox); + glUniformMatrix4fv(progSkybox.u_mvpMtx,1,GL_FALSE,glm::value_ptr(mvpMtx)); + s->Bind(); + meshCube->Draw(); + meshCube->Unbind(); +} + +void LogRenderInfo(){ + Log("%s %s\n",glGetString(GL_VENDOR),glGetString(GL_RENDERER)); + Log("%s\n",glGetString(GL_VERSION)); + Log("%s\n",glGetString(GL_SHADING_LANGUAGE_VERSION)); + //Log("%s\n",glGetString(GL_EXTENSIONS)); + //GLExtensions::Init(); + + const char *t = (const char*)glGetString(GL_EXTENSIONS); + const char *end = 0; + char ext[256]={0}; + int n=0; + while(*t){ + end = strstr(t," "); + if(end){ + strncpy(ext,t,end-t); + ext[end-t]=0; + } + else + break; + //Log("ext %d: %s\n",n,ext); + t=end+1; + n++; + } + Log("Exts count: %d\n",n); + + int rb, gb, bb; + glGetIntegerv(GL_RED_BITS, &rb); + glGetIntegerv(GL_GREEN_BITS, &gb); + glGetIntegerv(GL_BLUE_BITS, &bb); + int bits=-1; + glGetIntegerv(GL_ALPHA_BITS, &bits); + Log( "Color bits: %d %d %d %d\n", rb,gb,bb,bits); + glGetIntegerv(GL_DEPTH_BITS, &bits); + Log( "DEPTH BITS %d\n", bits); + glGetIntegerv(GL_STENCIL_BITS, &bits); + Log( "STENCIL BITS %d\n", bits); + + int rfmt=0; + int rtp=0; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT,&rfmt); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE,&rtp); + Log("Color read format %X, type %X\n",rfmt,rtp); + + +} + diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h new file mode 100644 index 0000000..8130864 --- /dev/null +++ b/src/renderer/renderer.h @@ -0,0 +1,76 @@ + +#pragma once + +#include "scene/Scene.h" +#include "renderer/camera.h" + +using glm::mat4; + +#define RENDERER_GUI 1 +#define RENDERER_LIGHT 2 +#define RENDERER_BACKBUFFER 4 + +class ResourceManager; +class Button; +struct submesh_t; +class glslProg; +class FrameBufferObject; + +class IRenderer; +IRenderer *CreateRenderer(); + +class IRenderer +{ +public: + IRenderer():width(256),height(256),aspect(1),scene(0),camera(0){} + virtual void Init(int initFlags,ResourceManager *resMan)=0; + virtual void Resize(int w, int h)=0; + virtual void Draw()=0; + + virtual void SetScene(Scene *sc){scene=sc;} + virtual void SetCamera(Camera *cam){camera=cam;} + virtual void SetColor(float r,float g,float b,float a)=0; + virtual void SetModelMtx(const glm::mat4 &mtx)=0; + virtual void Clear()=0; + virtual void DrawModel(Model *mdl,const glm::mat4 &mtx)=0; + virtual void Set2DMode(bool m = true)=0; + virtual void ResetViewport()=0; + virtual void DrawText(const char *t,float x,float y,float s)=0; + virtual void DrawRect(float x, float y, float w, float h)=0; + virtual void AddButton(Button *b)=0; + virtual void SetBackBufferScale(float s)=0; + virtual FrameBufferObject *GetFBO()=0; + virtual void RenderDepth(Camera *cam)=0; + virtual void DrawBBox(BoundingBox *bbox)=0; + virtual void DrawCube()=0; + + virtual void UseProg(glslProg *p){} + + int flags; + int width; + int height; + float aspect; + Scene *scene; + Camera *camera; + + glm::mat4 modelMtx; + glm::mat4 vpMtx; + glm::mat4 mvpMtx; + + std::vector buttons; + + bool debug; +}; + +class ILighting{ +public: + virtual void Init(IRenderer *rend)=0; + virtual void Draw()=0; + virtual void SetDirectional()=0; + virtual void SetCamera(Camera *cam)=0; + virtual void SetScene(Scene *s)=0; + virtual void AddLights(const mat4 &mtx, const submesh_t &submesh)=0; +}; + +ILighting *GetLighting(); + diff --git a/src/resource/ResourceManager.cpp b/src/resource/ResourceManager.cpp new file mode 100644 index 0000000..d05a795 --- /dev/null +++ b/src/resource/ResourceManager.cpp @@ -0,0 +1,120 @@ + +#include "log.h" +#include "resource/ResourceManager.h" +#include "renderer/Model.h" +#include "renderer/Material.h" +#include "graphics/texture.h" +#include "resource/nmf.h" +#include "resource/vtf.h" +#include "resource/dds.h" + +using namespace std; + +ResourceManager::ResourceManager(): models(),materials(),textures(), modelLoaders(),materialLoaders(),textureLoaders() +{} + +bool ResourceManager::Init() +{ + AddModelLoader(new NenuzhnoModelLoader(this)); + AddTextureLoader(new VTFLoader()); + AddTextureLoader(new DDSLoader()); + + return true; +} + +Model *ResourceManager::GetModel(const char *name) +{ + map::iterator fit = models.find(string(name)); + if(fit != models.end()) + return fit->second; + + for(set::iterator mlit = modelLoaders.begin(); mlit!=modelLoaders.end(); mlit++) + { + IModelLoader *ml = *mlit; + if(ml->CheckExt(name)){ + Model *mdl = ml->Load(name); + if(mdl){ + models[string(name)] = mdl; + }else{ + Log("ResourceManager::GetModel(%s) Not Found\n",name); + //TODO: defaultModel? + } + return mdl; + } + } + + Log("ResourceManager::GetModel(%s) Couldn't load Model\n",name); + return 0; +} + +IMaterial *ResourceManager::GetMaterial(const char *name) +{ + map::iterator fit = materials.find(string(name)); + if(fit != materials.end()) + return fit->second; + + for(set::iterator mlit = materialLoaders.begin(); mlit!=materialLoaders.end(); mlit++) + { + IMaterialLoader *ml = *mlit; + if(ml->CheckExt(name)){ + IMaterial *mat = ml->Load(name); + if(mat){ + materials[string(name)] = mat; + }else{ + Log("ResourceManager::GetMaterial(%s) Not Found\n",name); + //TODO: defaultMaterial? + } + return mat; + } + } + + Log("ResourceManager::GetMaterial(%s) Couldn't load Material\n",name); + return 0; +} + +Texture *ResourceManager::GetTexture(const char *name) +{ + map::iterator fit = textures.find(string(name)); + if(fit != textures.end()) + return fit->second; + + for(set::iterator tlit = textureLoaders.begin(); tlit!=textureLoaders.end(); tlit++) + { + ITextureLoader *tl = *tlit; + if(tl->CheckExt(name)){ + Texture *tex = tl->Load(name); + if(tex){ + textures[string(name)] = tex; + }else{ + Log("ResourceManager::GetTexture(%s) Not Found\n",name); + //TODO: defaultTexture? + } + return tex; + } + } + + Log("ResourceManager::GetTexture(%s) Couldn't load Texture\n",name); + return 0; +} + +void ResourceManager::AddMaterial(const char *name,IMaterial *mat){ + materials[string(name)] = mat; +} + +void ResourceManager::AddModelLoader(IModelLoader *loader) +{ + Log("ResourceManager::AddModelLoader(*.%s)\n",loader->GetExt()); + modelLoaders.insert(loader); +} + +void ResourceManager::AddMaterialLoader(IMaterialLoader *loader) +{ + Log("ResourceManager::AddMaterialLoader(*.%s)\n",loader->GetExt()); + materialLoaders.insert(loader); +} + +void ResourceManager::AddTextureLoader(ITextureLoader *loader) +{ + Log("ResourceManager::AddTextureLoader(*.%s)\n",loader->GetExt()); + textureLoaders.insert(loader); +} diff --git a/src/resource/ResourceManager.h b/src/resource/ResourceManager.h new file mode 100644 index 0000000..043b6dc --- /dev/null +++ b/src/resource/ResourceManager.h @@ -0,0 +1,61 @@ + +#pragma once + +#include +#include +#include + +class Model; +class IMaterial; +class Texture; +class ResourceManager; + +class IModelLoader{ +public: + IModelLoader(ResourceManager *rm):resMan(rm){} + virtual Model* Load(const char *name)=0; + virtual bool CheckExt(const char *name)=0; + virtual const char *GetExt()=0; + ResourceManager *resMan; +}; + +class IMaterialLoader{ +public: + IMaterialLoader(ResourceManager *rm):resMan(rm){} + virtual IMaterial* Load(const char *name)=0; + virtual bool CheckExt(const char *name)=0; + virtual const char *GetExt()=0; + ResourceManager *resMan; +}; +class ITextureLoader{ +public: + virtual Texture* Load(const char *name)=0; + virtual bool CheckExt(const char *name)=0; + virtual const char *GetExt()=0; +}; + +class ResourceManager{ +public: + ResourceManager(); + + bool Init(); + + Model *GetModel(const char *name); + IMaterial *GetMaterial(const char *name); + Texture *GetTexture(const char *name); + + void AddMaterial(const char *name,IMaterial *mat); + + void AddModelLoader(IModelLoader *loader); + void AddMaterialLoader(IMaterialLoader *loader); + void AddTextureLoader(ITextureLoader *loader); + +private: + std::map models; + std::map materials; + std::map textures; + + std::set modelLoaders; + std::set materialLoaders; + std::set textureLoaders; +}; diff --git a/src/resource/dds.h b/src/resource/dds.h new file mode 100644 index 0000000..0183d29 --- /dev/null +++ b/src/resource/dds.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "resource/ResourceManager.h" + +#define DDS_IDENT 542327876 +#define DDS_DXT1 827611204 +#define DDS_DXT5 894720068 + +struct DDS_PIXELFORMAT +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwFourCC; + uint32_t dwRGBBitCount; + uint32_t dwRBitMask; + uint32_t dwGBitMask; + uint32_t dwBBitMask; + uint32_t dwABitMask; +}; + +struct DDS_HEADER +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwHeight; + uint32_t dwWidth; + uint32_t dwPitchOrLinearSize; + uint32_t dwDepth; + uint32_t dwMipMapCount; + uint32_t dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t dwCaps; + uint32_t dwCaps2; + uint32_t dwCaps3; + uint32_t dwCaps4; + uint32_t dwReserved2; +}; + +class DDSLoader: public ITextureLoader{ +public: + virtual Texture *Load(const char *name); + virtual bool CheckExt(const char *name); + virtual const char *GetExt(){return "dds";} +}; diff --git a/src/resource/dds_loader.cpp b/src/resource/dds_loader.cpp new file mode 100644 index 0000000..c92135f --- /dev/null +++ b/src/resource/dds_loader.cpp @@ -0,0 +1,123 @@ + +#include +#include +#include +using namespace std; +#include + +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/texture.h" +#include "resource/dds.h" + +bool DDSLoader::CheckExt(const char *name){ + return strstr(name,".dds")!=0; +} + +//bool Texture::LoadDDS(const char *fileName, int mip){ +Texture *DDSLoader::Load(const char *fileName){ + string filePath = string(fileName); + if(filePath.find(".dds") == string::npos) + filePath += ".dds"; + + //Log("Load DDS Texture %s\n",filePath.c_str()); + + IFile *input = g_fs.Open(filePath.c_str()); + if(!input){ + //LOG( "File: %s not found\n", path); + return 0; + } + + int ident=0; + input->Read(&ident, 4); + if(ident!=DDS_IDENT){ + Log("LoadDDS Invalid dds file (%s)\n", fileName); + g_fs.Close(input); + return 0; + } + + DDS_HEADER header; + input->Read(&header, sizeof(DDS_HEADER)); + + //LOG("LoadDDS %s: size %dx%d, mips %d, format %d\n",fileName,header.dwWidth,header.dwHeight,header.dwMipMapCount,header.ddspf.dwFourCC); + + Texture *tex = new Texture(); + + tex->width = header.dwWidth; + tex->height = header.dwHeight; + /*if(tex->width < 4) + tex->width = 4; + if(tex->height < 4) + tex->height = 4;*/ + + size_t texSize = 0; + + int ifmt; + //int fmt; + //int type = GL_UNSIGNED_BYTE; + bool compressed = false; + if(header.ddspf.dwFourCC == DDS_DXT1){//4bpp + texSize = ((tex->width + 3) / 4) * ((tex->height + 3) / 4) * 8; + ifmt = GL_COMPRESSED_RGB_S3TC_DXT1; + //fmt = GL_RGB; + compressed = true; + }else if(header.ddspf.dwFourCC == DDS_DXT5){//8bpp + texSize = ((tex->width + 3)/4)*((tex->height+3)/4)*16; + ifmt = GL_COMPRESSED_RGBA_S3TC_DXT5; + compressed = true; + }else{ + if(header.ddspf.dwFourCC==0){ + if(header.ddspf.dwRGBBitCount==24){ + texSize = tex->width*tex->height*3; + ifmt = FMT_BGR8; + }else if(header.ddspf.dwRGBBitCount==8){ + texSize = tex->width*tex->height; + ifmt = GL_LUMINANCE; + }else{ + Log("DDS: fourCC is 0, size %d, flags %X, bitCount %d, mask %X%X%X%X\n",header.ddspf.dwSize, + header.ddspf.dwFlags, header.ddspf.dwRGBBitCount, header.ddspf.dwRBitMask, header.ddspf.dwGBitMask, header.ddspf.dwBBitMask, header.ddspf.dwABitMask); + } + } + } + if(texSize==0){ + g_fs.Close(input); + delete tex; + char fourCC[5]={0}; + memcpy(fourCC,&header.ddspf.dwFourCC,4); + Log("LoadDDS (%s) Unsuported format %s(%d)\n", fileName, fourCC,header.ddspf.dwFourCC); + return 0; + } + + GLubyte *texData = new GLubyte[texSize]; + //input.seekg(offset); + input->Read(texData, texSize); + g_fs.Close(input); + + if(ifmt==FMT_BGR8){ + ResampleBGR(texData,texSize); + ifmt = GL_RGB; + } + + tex->Create(tex->width, tex->height); + tex->SetWrap(GL_REPEAT); + tex->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + +#ifndef ANDROID + glTexParameteri(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); +#endif + if(compressed){ + tex->UploadCompressed(ifmt, texSize, texData); + }else{ + tex->Upload(0,ifmt,texData); + } + if(CheckGLError("LoadDDS Upload", __FILE__, __LINE__)) + Log("GL error while loading %s\n", fileName); + + glGenerateMipmap(tex->target); + + delete[] texData; + + return tex; +} diff --git a/src/resource/mesh_loader.cpp b/src/resource/mesh_loader.cpp new file mode 100644 index 0000000..e9b9c0e --- /dev/null +++ b/src/resource/mesh_loader.cpp @@ -0,0 +1,35 @@ + +#include +using namespace std; +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/platform_gl.h" +#include "graphics/ArrayBuffer.h" +#include "renderer/mesh.h" + +Mesh* LoadMeshFile(const char* fileName, bool vbo) +{ + string filePath = "models/"+string(fileName)+".mesh"; + IFile *file = g_fs.Open(filePath.c_str()); + if(!file){ + //Log("File: %s not found!\n", path); + return 0; + } + + int numVerts = 0; + file->Read(&numVerts, 4);//tris + numVerts*=3; + Log("LoadMesh %s numverts %d\n", fileName, numVerts); + GLfloat *verts = new GLfloat[numVerts*8]; + file->Read(verts, numVerts*32); + g_fs.Close(file); + + Mesh *out = 0; + if(vbo){ + out = new MeshFBO_N3_T2(verts, numVerts, GL_TRIANGLES); + delete[] verts; + }else{ + out = new Mesh_N3_T2(verts, numVerts, GL_TRIANGLES); + } + return out; +} diff --git a/src/resource/nmf.h b/src/resource/nmf.h new file mode 100644 index 0000000..6ec1a31 --- /dev/null +++ b/src/resource/nmf.h @@ -0,0 +1,110 @@ + +#pragma once + +#include "cull/BoundingBox.h" +#include "renderer/Model.h" + +using glm::vec3; + +#define NMF_MAGIC ((0<<24)+('F'<<16)+('M'<<8)+'N') + +//major ver - not compat +//minor ver - compat +struct nmfHeaderBase_t{ + int magic; + uint16_t version[2]; + int length; + int hash; + int flags; +}; + +//v1.0 +struct nmfHeader1_t{ + int vertexFormat; //bit mask pos,nrm,uv,uv2,tangents,weights, etc. (packet types?) + int vertexStride; + int vertexCount; + int vertexOffset; + + int indexSize; + int indexCount; + int indexOffset; + + int submeshCount; + int submeshOffset; + + int materialsCount; + int materialsOffset; +}; +//v 1.1 +struct nmfHeader11_t{ + int collidersCount; + int collidersOffset; + + BoundingBox bbox; +}; + +//v2.0 +struct nmfHeader2_t{ + BoundingBox bbox; + + int vertAttribCount; + int vertexCount; + int vertexDataLength; + int vertexOffset; + + int indexSize; + int indexCount; + int indexOffset; + + int submeshCount; + int submeshOffset; + + int materialsCount; + int materialsOffset; + + int collidersCount; + int collidersOffset; +}; + +//TODO: +/*Bones +Sequences (+external) +Attachments (to bones) +*/ + +struct nmfVertAttrib_t{ + uint32_t offset; + uint16_t type; + uint8_t id; + uint8_t stride; + + operator vertAttrib_t() const{ + int size = (type&3)+1; + int otype = 0; + if((type&32)==0){ + if((type&16)==0) + otype = GL_BYTE; + else + otype = GL_SHORT; + }else{ + if((type&16)==0) + otype = GL_FLOAT; + else + otype = GL_HALF_FLOAT; + } + return vertAttrib_t(id,size,otype,type&8,stride,offset); + } +}; + +//nmf_loader.cpp + +#include "resource/ResourceManager.h" + +class NenuzhnoModelLoader: public IModelLoader{ +public: + NenuzhnoModelLoader(ResourceManager *rm):IModelLoader(rm){} + virtual Model *Load(const char *name); + virtual bool CheckExt(const char *name); + virtual const char *GetExt(){return "nmf";} +}; + diff --git a/src/resource/nmf_loader.cpp b/src/resource/nmf_loader.cpp new file mode 100644 index 0000000..bdc6474 --- /dev/null +++ b/src/resource/nmf_loader.cpp @@ -0,0 +1,166 @@ + +#include +#include +#include +using namespace std; + +#include "log.h" +#include "system/FileSystem.h" +#include "renderer/Model.h" +#include "graphics/texture.h" +#include "resource/nmf.h" + +bool NenuzhnoModelLoader::CheckExt(const char *name){ + return strstr(name,".nmf")!=0; +} + +//Model *LoadNenuzhnoModel(const char *fileName,int flags){ +Model *NenuzhnoModelLoader::Load(const char *fileName){//,int flags + string filePath = "models/"+string(fileName); + IFile *file = g_fs.Open(filePath.c_str()); + if(!file){ + return 0; + } + + nmfHeaderBase_t headerBase; + nmfHeader2_t head2; + file->Read(&headerBase,sizeof(nmfHeaderBase_t)); + if(headerBase.magic!=NMF_MAGIC){ + Log("Load NMF: Error invalid magic %d (%s)\n", headerBase.magic, fileName); + g_fs.Close(file); + return nullptr; + } + //TODO: check length + //TODO: check hash + + Log("Loading model (%s) ver %d.%d\n",fileName,headerBase.version[0],headerBase.version[0]); + if(headerBase.version[0]==1){ + nmfHeader1_t head1; + file->Read(&head1,sizeof(nmfHeader1_t)); + + //Convert old header to new + //head2.bbox //calc later + head2.vertexCount = head1.vertexCount; + head2.vertexDataLength = head1.vertexCount * head1.vertexStride; + head2.vertexOffset = head1.vertexOffset; + head2.indexSize = head1.indexSize; + head2.indexCount = head1.indexCount; + head2.indexOffset = head1.indexOffset; + head2.submeshCount = head1.submeshCount; + head2.submeshOffset = head1.submeshOffset; + head2.materialsCount = head1.materialsCount; + head2.materialsOffset = head1.materialsOffset; + //head1.vertexStride //now unused + + head2.collidersCount = 0; + if(headerBase.version[1]>=1){ + nmfHeader11_t head11; + file->Read(&head11,sizeof(nmfHeader11_t)); + head2.bbox = head11.bbox; + head2.collidersCount = head11.collidersCount; + head2.collidersOffset = head11.collidersOffset; + } + }else if(headerBase.version[0]==2){ + file->Read(&head2,sizeof(nmfHeader2_t)); + }else{ + Log("Load NMF: Error invalid version %d.%d (%s)\n", headerBase.version[0], headerBase.version[1], fileName); + g_fs.Close(file); + return nullptr; + } + + //Log("Load NMF: %s\n", fileName); + + Model *model = new Model(); + + file->Seek(head2.vertexOffset); + if(headerBase.version[0]==2){ + nmfVertAttrib_t *attribs = new nmfVertAttrib_t[head2.vertAttribCount]; + file->Read(attribs, head2.vertAttribCount*sizeof(nmfVertAttrib_t)); + model->vertexFormat.Resize(head2.vertAttribCount); + for(int i=0; ivertexFormat[i] = attribs[i]; + } + delete[] attribs; + }else{ + int stride = head2.vertexDataLength/head2.vertexCount; + model->vertexFormat.Resize(3); + model->vertexFormat[0]={0,3,GL_FLOAT,false,stride,0};//pos + model->vertexFormat[1]={1,3,GL_FLOAT,false,stride,12};//norm + model->vertexFormat[2]={2,2,GL_FLOAT,false,stride,24};//uv + } + char *verts = new char[head2.vertexDataLength]; + file->Read(verts,head2.vertexDataLength); + + model->vbo.Create(); + model->vbo.Upload(head2.vertexDataLength,verts); + //delete[] verts;//need for bbox + model->vertexCount = head2.vertexCount; + //model->vertexStride = header.vertexStride; + + if(head2.indexCount){ + model->indexSize = head2.indexSize; + if(head2.indexSize==1){ + model->indexType = GL_UNSIGNED_BYTE; + }else if(head2.indexSize==2){ + model->indexType = GL_UNSIGNED_SHORT; + }else if(head2.indexSize==4){ + //TODO: check oes uint extension + model->indexType = GL_UNSIGNED_INT; + }else{ + Log("Load NMF: invelid index size %d (%s)\n",head2.indexSize,fileName); + } + int len = head2.indexCount*head2.indexSize; + char *inds = new char[len]; + file->Seek(head2.indexOffset); + file->Read(inds,len); + + model->ibo.Create(); + model->ibo.Upload(len,inds); + delete[] inds; + } + + if(head2.submeshCount){ + model->submeshes.Resize(head2.submeshCount); + file->Seek(head2.submeshOffset); + file->Read(model->submeshes.data,sizeof(submesh_t)*head2.submeshCount); + }else{ + Log("Model %s haven't submeshes\n",fileName); + } + + if(head2.materialsCount){ + int *matOffsets = new int[head2.materialsCount]; + file->Seek(head2.materialsOffset); + file->Read(matOffsets,sizeof(int)*head2.materialsCount); + + model->materials.Resize(head2.materialsCount); + //Log("Load NMF: materialsCount %d\n",header.materialsCount); + for(int i=0;iSeek(matOffsets[i]); + int len; + file->Read(&len,sizeof(int)); + char temp[len]={0}; + file->Read(temp,len); + model->materials[i].name = temp; + //Log("Load NMF: material %d offs %d len %d name %s\n",i,matOffsets[i],len,temp); + model->materials[i].mat = resMan->GetMaterial(temp); + } + delete[] matOffsets; + } + + if(headerBase.version[0]==1&&headerBase.version[1]==0){ + //vertexFormat[0] must be position + model->bbox.FromVerts(verts,head2.vertexCount,model->vertexFormat[0]); + }else{ + model->bbox = head2.bbox; + if(head2.collidersCount){ + model->colliders.Resize(head2.collidersCount); + file->Seek(head2.collidersOffset); + file->Read(model->colliders.data,sizeof(nmfCollider_t)*head2.collidersCount); + } + //Log("NMF (%s) Generate bbox (min: %.2f %.2f %.2f, max: %.2f %.2f %.2f)\n",fileName,model->bbox.min.x,model->bbox.min.y,model->bbox.min.z,model->bbox.max.x,model->bbox.max.y,model->bbox.max.z); + } + delete[] verts; + + g_fs.Close(file); + return model; +} diff --git a/src/resource/obj_loader.cpp b/src/resource/obj_loader.cpp new file mode 100644 index 0000000..64c061c --- /dev/null +++ b/src/resource/obj_loader.cpp @@ -0,0 +1,186 @@ + +#include +#include +#include "stdlib.h" +#include "string.h" +using namespace std; +#include +#include +#include +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/platform_gl.h" +#include "renderer/mesh.h" + +glm::vec3 atovec3(const char *str) +{ + glm::vec3 out(0); + if(str[0]=='[') + str++; + if(str[0]==' ') + str++; + /* + out.x = atof(str); + str = strstr(str, " ")+1; + out.y = atof(str); + str = strstr(str, " ")+1; + out.z = atof(str); + */ + sscanf(str, "%f %f %f", &out.x, &out.y, &out.z); + + return out; +} + +glm::vec2 atovec2(char *str) +{ + glm::vec2 out(0); + if(str[0]=='[') + str++; + if(str[0]==' ') + str++; + /* + out.x = atof(str); + str = strstr(str, " ")+1; + out.y = atof(str); + */ + sscanf(str, "%f %f", &out.x, &out.y); + + return out; +} + +struct vert_t +{ + glm::vec3 pos; + glm::vec3 normal; + glm::vec2 uv; +}; + +Mesh* LoadObjFile(const char* fileName, bool vbo) +{ + string filePath = "models/"+string(fileName)+".obj"; + IFile *file = g_fs.Open(filePath.c_str()); + if(!file){ + //Log("File: %s not found!\n", path); + return 0; + } + + vector verts; + vector normals; + vector uvs; + vector faces; + + string line; + while(!file->eof()) + { + file->GetLine(line); + if(line[0]=='#') + continue; + if(line.empty()) + continue; + + if(line[0]=='v') + { + if(line[1]==' ') + { + verts.push_back(atovec3(&line[2])); + continue; + } + if(line[1]=='n') + { + normals.push_back(atovec3(&line[3])); + continue; + } + if(line[1]=='t') + { + uvs.push_back(atovec2(&line[3])); + continue; + } + } + + if(line[0]=='f') + { + int ofs=2; + int nf=0; + int f1v,f1t,f1n, f2v,f2t,f2n, f3v,f3t,f3n; + while((unsigned int)ofs-1!=string::npos) + { + int attribs = sscanf(line.c_str()+ofs,"%d/%d/%d",&f1v,&f1t,&f1n); + if(attribs<2){ + attribs = sscanf(line.c_str()+ofs,"%d//%d",&f1v,&f1n); + f1t=0; + } + ofs=line.find(' ',ofs)+1; + if(nf==0) + { + f2v=f1v; + f2t=f1t; + f2n=f1n; + } + if(nf>2) + { + faces.push_back(f2v); + faces.push_back(f2t); + faces.push_back(f2n); + faces.push_back(f3v); + faces.push_back(f3t); + faces.push_back(f3n); + //Log("nf %d ",nf); + } + faces.push_back(f1v); + faces.push_back(f1t); + faces.push_back(f1n); + f3v=f1v; + f3n=f1n; + f3t=f1t; + nf++; + } + continue; + } + + //Log("%s\n",line.c_str()); + } + g_fs.Close(file); + Log("load %s: %d verts %d normals %d uvs %d faces\n",fileName,verts.size(),normals.size(),uvs.size(),faces.size()/9); + + bool genNormals = (normals.size()==0); + int numVerts = faces.size()/3; + int numTris = numVerts/3; + float *outVerts = new float[numVerts*8]; + //Log("outVerts = %p\n",outVerts); + glm::vec3 normal(0.0f); + for(int t=0; tpos = verts[faces[(t*3+i)*3]-1]; + //Log("Set pos (%f %f %f)\n",verts[faces[(t*3+i)*3]-1].x,verts[faces[(t*3+i)*3]-1].y,verts[faces[(t*3+i)*3]-1].z); + if(genNormals) + v->normal = normal; + else + v->normal = glm::normalize(normals[faces[(t*3+i)*3+2]-1]); + //Log("Set normal (%f %f %f)\n",normals[faces[(t*3+i)*3+1]-1].x,normals[faces[(t*3+i)*3+1]-1].y,normals[faces[(t*3+i)*3+1]-1].z); + + int uvId = faces[(t*3+i)*3+1]-1; + if(uvId>0) + v->uv = uvs[uvId]; + else + v->uv = glm::vec2(0); + //Log("Set uv (%f %f)\n",uvs[faces[(t*3+i)*3+2]-1].x,uvs[faces[(t*3+i)*3+2]-1].y); + + //if((t*3+i)<24) + // Log("vert %d %f %f %f\n",(t*3+i),v->pos.x,v->pos.y,v->pos.z); + } + } + + return new Mesh_N3_T2(outVerts, numVerts, GL_TRIANGLES); +} + diff --git a/src/resource/vtf.h b/src/resource/vtf.h new file mode 100644 index 0000000..bbbc7ac --- /dev/null +++ b/src/resource/vtf.h @@ -0,0 +1,96 @@ +#pragma once + +#pragma pack(1) +struct VTFFileBaseHeader_t +{ + char fileTypeString[4]; // "VTF" Valve texture file + int version[2]; // version[0].version[1] + int headerSize; +}; + +enum ImageFormat +{ + IMAGE_FORMAT_UNKNOWN = -1, + IMAGE_FORMAT_RGBA8888 = 0, + IMAGE_FORMAT_ABGR8888, + IMAGE_FORMAT_RGB888, + IMAGE_FORMAT_BGR888, + IMAGE_FORMAT_RGB565, + IMAGE_FORMAT_I8,//5 + IMAGE_FORMAT_IA88, + IMAGE_FORMAT_P8, + IMAGE_FORMAT_A8, + IMAGE_FORMAT_RGB888_BLUESCREEN, + IMAGE_FORMAT_BGR888_BLUESCREEN,//10 + IMAGE_FORMAT_ARGB8888, + IMAGE_FORMAT_BGRA8888, + IMAGE_FORMAT_DXT1, + IMAGE_FORMAT_DXT3, + IMAGE_FORMAT_DXT5,//15 + IMAGE_FORMAT_BGRX8888, + IMAGE_FORMAT_BGR565, + IMAGE_FORMAT_BGRX5551, + IMAGE_FORMAT_BGRA4444, + IMAGE_FORMAT_DXT1_ONEBITALPHA,//20 + IMAGE_FORMAT_BGRA5551, + IMAGE_FORMAT_UV88,//22 + IMAGE_FORMAT_UVWQ8888, + IMAGE_FORMAT_RGBA16161616F, + IMAGE_FORMAT_RGBA16161616, + IMAGE_FORMAT_UVLX8888, + IMAGE_FORMAT_R32F, // Single-channel 32-bit floating point + IMAGE_FORMAT_RGB323232F, + IMAGE_FORMAT_RGBA32323232F, + + // Depth-stencil texture formats for shadow depth mapping + IMAGE_FORMAT_NV_DST16,//30 // + IMAGE_FORMAT_NV_DST24, // + IMAGE_FORMAT_NV_INTZ, // Vendor-specific depth-stencil texture + IMAGE_FORMAT_NV_RAWZ, // formats for shadow depth mapping + IMAGE_FORMAT_ATI_DST16, // + IMAGE_FORMAT_ATI_DST24, // + IMAGE_FORMAT_NV_NULL, // Dummy format which takes no video memory + + // Compressed normal map formats + IMAGE_FORMAT_ATI2N, // One-surface ATI2N / DXN format + IMAGE_FORMAT_ATI1N, // Two-surface ATI1N format + + NUM_IMAGE_FORMATS +}; + +struct VTFFileHeaderV7_1_t : public VTFFileBaseHeader_t +{ + unsigned short width; + unsigned short height; + unsigned int flags; + unsigned short numFrames; + unsigned short startFrame; + char pad1[4]; + //glm::vec3 reflectivity; + float reflectivity[3]; + char pad2[4]; + float bumpScale; + ImageFormat imageFormat; + unsigned char numMipLevels; + ImageFormat lowResImageFormat; + unsigned char lowResImageWidth; + unsigned char lowResImageHeight; +}; + +struct VTFFileHeader_t : public VTFFileHeaderV7_1_t +{ + +}; +#pragma pack() + +size_t computeMipsSize(int width, int height, int mipcount, float bpp); + +#include "resource/ResourceManager.h" + +class VTFLoader: public ITextureLoader{ +public: + virtual Texture *Load(const char *name); + virtual Texture *Load(const char *name, int lod); + virtual bool CheckExt(const char *name); + virtual const char *GetExt(){return "vtf";} +}; diff --git a/src/resource/vtf_loader.cpp b/src/resource/vtf_loader.cpp new file mode 100644 index 0000000..86af3c8 --- /dev/null +++ b/src/resource/vtf_loader.cpp @@ -0,0 +1,262 @@ + +#include +#include +#include +using namespace std; + +#include "log.h" +#include "system/FileSystem.h" +#include "graphics/platform_gl.h" +#include "graphics/gl_utils.h" +#include "graphics/texture.h" +#include "resource/vtf.h" + +size_t computeMipsSize(int width, int height, int mipcount, float bpp); + +bool VTFLoader::CheckExt(const char *name) +{ + return strstr(name,".vtf")!=0; +} + +Texture *VTFLoader::Load(const char *fileName) +{ + return Load(fileName,0); +} + +Texture *VTFLoader::Load(const char *fileName, int mip) +{ + string filePath = "materials/"+string(fileName); + if(filePath.find(".vtf") == string::npos) + filePath += ".vtf"; + + IFile *input = g_fs.Open(filePath.c_str()); + + if(!input){ + //LOG( "File: %s not found\n", path); + return 0; + } + + VTFFileHeader_t header; + input->Read(&header, sizeof(VTFFileHeader_t)); + + if(header.width!=header.height){ + //LOG("%s(%dx%d) is not quad\n",fileName,header.width,header.height); + } + + if(header.flags&0x4000) + mip = 0; + if(mip >= header.numMipLevels) + mip = header.numMipLevels-1; + + Texture *tex = new Texture(); + + tex->width = header.width>>mip; + tex->height = header.height>>mip; + if(tex->width < 4) + tex->width = 4; + if(tex->height < 4) + tex->height = 4; + + size_t texSize = 0; + size_t offset = header.headerSize; + int faces = 1; + if(header.flags&0x4000){ + if(header.version[1] < 5) + faces = 7; + else + faces = 6; + }else{ + offset += ((header.lowResImageWidth+3)/4)*((header.lowResImageHeight+3)/4)*8; + } + int ifmt; + int fmt; + int type = GL_UNSIGNED_BYTE; + bool compressed = false; + if(header.imageFormat == IMAGE_FORMAT_DXT1){//4bpp + texSize = ((tex->width + 3) / 4) * ((tex->height + 3) / 4) * 8; + offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,0.5f)*faces; + ifmt = GL_COMPRESSED_RGB_S3TC_DXT1; + fmt = GL_RGB; + compressed=true; + }else if(header.imageFormat == IMAGE_FORMAT_DXT5){//8bpp + texSize = ((tex->width + 3) / 4) * ((tex->height + 3) / 4) * 16; + offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,1)*faces; + ifmt = GL_COMPRESSED_RGBA_S3TC_DXT5; + fmt = GL_RGBA; + compressed=true; + }else if(header.imageFormat == IMAGE_FORMAT_RGBA16161616F){//64bpp + Log("image format RGBA16F (%s)\n",fileName); + texSize = tex->width * tex->height * 8; + offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,8)*faces; + ifmt = GL_RGBA16F; + fmt = GL_RGBA; + type = GL_HALF_FLOAT; + }else if(header.imageFormat == IMAGE_FORMAT_BGRA8888){//32bpp + Log("image format BGRA8 (%s)\n",fileName); + texSize = tex->width * tex->height * 4; + offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,4)*faces; + ifmt = GL_RGBA; + fmt = GL_BGRA; + }else if(header.imageFormat == IMAGE_FORMAT_BGR888){//24bpp + Log("image format BGR8 (%s)\n",fileName); + texSize = tex->width * tex->height * 3; + offset += computeMipsSize(tex->width,tex->height,header.numMipLevels-mip,3)*faces; + ifmt = GL_RGB; + fmt = FMT_BGR8; + //need resample + }else{ + Log("Unknown image format %d (%s)\n", header.imageFormat,fileName); + g_fs.Close(input); + delete tex; + return 0; + } + + //LOG("texSize %d offset %d\n", texSize, offset); + + GLubyte *texData = new GLubyte[texSize*faces]; + input->Seek(offset); + input->Read(texData, texSize*faces); + g_fs.Close(input); + + //add resample flag or something like that + if(fmt==FMT_BGR8){ + ResampleBGR(texData,texSize*faces); + fmt=GL_RGB; + } + + //TODO extension + if(fmt==GL_BGRA){ + ResampleBGRA(texData,texSize*faces); + fmt=GL_RGBA; + } + + if(header.flags&0x4000){ + //LOG("%s is a cubemap! size %dx%d mips %d fmt %d ifmt %x type %x\n",fileName,width,height,header.numMipLevels, header.imageFormat,ifmt,type); + tex->target = GL_TEXTURE_CUBE_MAP; + tex->Create(tex->width, tex->height); + tex->SetFilter(GL_LINEAR, GL_LINEAR); + if(CheckGLError("LoadVTF cubemap create", __FILE__, __LINE__)) + Log("GL error while loading %s\n",fileName); + + glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + //glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + CheckGLError("LoadVTF cubemap Parameters", __FILE__, __LINE__); + + if(!compressed){ + for(int i=0;i<6;i++){ + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, ifmt, tex->width, tex->height, 0, fmt, type, texData+texSize*i); + } + }else{ +//#ifdef ANDROID + //GLubyte *newData = new GLubyte[width*height*4]; + for(int i=0;i<6;i++){ + tex->target=GL_TEXTURE_CUBE_MAP_POSITIVE_X+i; + tex->UploadCompressed(ifmt,texSize,texData+texSize*i); + //DecompressDXT(this, texData+texSize*i, newData, texSize, header.imageFormat); + //glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, fmt, width, height, 0, fmt, type, newData); + } + tex->target = GL_TEXTURE_CUBE_MAP; + //delete[] newData; +/*#else + for(int i=0;i<6;i++) + { + glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,ifmt, width, height, 0, texSize, texData+texSize*i); + //glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, ifmt, width, height, 0, fmt, type, texData+texSize*i); + } +#endif*/ + + } + tex->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + glGenerateMipmap(tex->target); + CheckGLError("LoadVTF env upload", __FILE__, __LINE__); + }else{ + tex->Create(tex->width, tex->height); + tex->SetWrap(GL_REPEAT); + tex->SetFilter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + +#ifndef ANDROID + glTexParameteri(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); +#endif + //if(header.imageFormat == IMAGE_FORMAT_DXT1||header.imageFormat == IMAGE_FORMAT_DXT5) + if(compressed){ + //Upload(0, GL_RGBA_DXT5_S3TC, texData); + tex->UploadCompressed(ifmt,texSize,texData); + }else if (header.imageFormat==IMAGE_FORMAT_BGRA8888||header.imageFormat==IMAGE_FORMAT_BGR888){ + tex->Upload(0, ifmt,fmt,type, texData); + } + else + Log("wut?\n"); + if(CheckGLError("LoadVTF Upload", __FILE__, __LINE__)) + Log("GL error while loading %s\n",fileName); + glGenerateMipmap(tex->target); + } + + delete[] texData; + + return tex; +} + +size_t computeMipsSize(int width, int height, int mipcount, float bpp) +{ + size_t imgSize = 0; + + width>>=1; + height>>=1; + if(bpp<3) + { + if(width < 4) + width = 4; + + if(height < 4) + height = 4; + } + for(int i=0; i>=1; + height>>=1; + if(bpp<3) + { + if(width < 4) + width = 4; + + if(height < 4) + height = 4; + } + } + + return imgSize; +} + +/* +size_t computeMipsSize(int width, int height, int mipcount, float bpp) +{ + size_t imgSize = 0; + + width>>=1; + height>>=1; + if(width < 4) + width = 4; + + if(height < 4) + height = 4; + + for(int i=1; i>=1; + height>>=1; + + if(width < 4) + width = 4; + + if(height < 4) + height = 4; + } + + return imgSize; +} +*/ diff --git a/src/scene/Scene.cpp b/src/scene/Scene.cpp new file mode 100644 index 0000000..87c5d5a --- /dev/null +++ b/src/scene/Scene.cpp @@ -0,0 +1,67 @@ + +#include +#include "log.h" +#include "renderer/renderer.h" +#include "scene/Scene.h" + +Scene::Scene():objects(),dynamicObjects(),skyBox(0){ + +} + +void Scene::Update(float delta){ + for(uint32_t i=0;iUpdate(delta); + } +} + +void Scene::Draw(IRenderer *r){ + for(uint32_t i=0;iDraw(r); + } + + for(uint32_t i=0;iDraw(r); + } +} + +void Scene::AddObject(SceneObject *obj){ + if(obj->dynamic) + dynamicObjects.push_back(obj); + else + objects.push_back(obj); +} + +void Scene::RemoveObject(SceneObject *obj){ + for(std::vector::iterator it = dynamicObjects.begin();it!=dynamicObjects.end();it++){ + if(*it==obj){ + dynamicObjects.erase(it); + return; + } + } + Log("RemoveObject(%p) can't find\n",obj); +} + +SceneObject *Scene::Find(const char *name){ + for(std::vector::iterator it = dynamicObjects.begin();it!=dynamicObjects.end();it++){ + if((*it)->name==name){ + return *it; + } + } + Log("Find(%s) can't find\n",name); + return 0; +} + +void Scene::AddLight(LightObject *l){ + lights.push_back(l); +} + +StaticModel::StaticModel():SceneObject(){ + dynamic = false; + mdl = 0; +} + +void StaticModel::Draw(IRenderer *r){ + if(!mdl) + return; + r->DrawModel(mdl,modelMtx); +} diff --git a/src/scene/Scene.h b/src/scene/Scene.h new file mode 100644 index 0000000..0835471 --- /dev/null +++ b/src/scene/Scene.h @@ -0,0 +1,81 @@ + +#pragma once + +#include +#include +#include +#include + +class IRenderer; +class Model; +class Texture; + +class SceneObject +{ +public: + SceneObject():modelMtx(1.0f), parent(0), dynamic(false),name(){ + } + virtual void Draw(IRenderer *r){} + virtual void Update(float delta){} + void SetMtx(glm::mat4 &mtx){ + modelMtx = mtx; + } + + glm::mat4 modelMtx; + SceneObject *parent; + bool dynamic;//TODO flags + std::string name; +}; + +enum eLightType +{ + eDirectional=1, + ePoint=2, + eSpot=3 +}; + +class LightObject +{ +public: + LightObject():type(ePoint),pos(),dir(),color(),radius(5),fov(90){} + LightObject(eLightType t,glm::vec3 p, glm::vec3 c,float r):type(t),pos(p),color(c),radius(r),fov(90){} + eLightType type; + glm::vec3 pos; + glm::vec3 dir; + glm::vec3 color; + float radius; + float fov; +}; + +class StaticModel:public SceneObject +{ +public: + StaticModel(); + + virtual void Draw(IRenderer *r); + Model *mdl; +}; + +class Scene +{ +public: + Scene(); + + void Update(float delta); + void Draw(IRenderer *r); + void AddObject(SceneObject *obj); + //SceneObject *NewObject(); + void RemoveObject(SceneObject *obj); + SceneObject *Find(const char *name); + void AddLight(LightObject *l); + + std::vector objects; + std::vector dynamicObjects; + std::vector lights; + + glm::vec3 startPos; + glm::vec3 startRot; + Texture *skyBox;//cubemap + glm::vec3 sunDirection; +}; + diff --git a/src/sound/AudioClip.cpp b/src/sound/AudioClip.cpp new file mode 100644 index 0000000..7caee71 --- /dev/null +++ b/src/sound/AudioClip.cpp @@ -0,0 +1,156 @@ + +#include +#include +#include +using namespace std; + +#include "log.h" +#include "system/FileSystem.h" +#include "AudioClip.h" + +AudioClip::AudioClip():buffer(0),length(0),freq(0),channels(0) +{} + +AudioClip::~AudioClip() +{ + if(buffer){ + //Log("destroy clip\n"); + delete[] (char*)buffer; + buffer = 0; + } +} + +struct wavHeader_t{ + int RIFF; + int fileSize; + int WAVE; + int fmtCC; + int fmtSize;//16 + uint16_t fmt; + uint16_t channels; + int freq; + int byteRate; + uint16_t blockAlign; + uint16_t width; +}; + +bool AudioClip::LoadFromFile(const char *fileName) +{ + string filePath = "sound/"+string(fileName); + IFile *input = g_fs.Open(filePath.c_str()); + if(!input){ + //Log( "File: %s not found\n", path); + return false; + } + + wavHeader_t header; + int fourCC; + int chunkSize; +/* + int fmt=0; + int width=0; + input.read((char*)&fourCC, 4);//RIFF + input.read((char*)&chunkSize, 4); + //Log("%s chunk size %d\n",(char*)&fourCC, chunkSize); + input.read((char*)&fourCC, 4);//WAVE + input.read((char*)&fourCC, 4);//"fmt " + input.read((char*)&chunkSize, 4); + //Log("%s chunk size %d\n",(char*)&fourCC, chunkSize); + input.read((char*)&fmt, 2); + input.read((char*)&channels, 2); + input.read((char*)&freq, 4); + input.seekg(6,ios_base::cur); + input.read((char*)&width, 2); +*/ + input->Read(&header,sizeof(wavHeader_t)); + freq = header.freq; + channels = header.channels; + width = header.width; + Log("Load sound %s: fmt %d, channels %d, freq %d, width %d\n", fileName, header.fmt, header.channels, header.freq, header.width); + + input->Read(&fourCC, 4);//data + if(fourCC!=0x61746164){ + if(fourCC==0x5453494C){ + input->Read(&chunkSize, 4); + input->Seek(chunkSize,eCur); + input->Read(&fourCC, 4); + }else{ + //input.seekg(input.tellg()-2,ios_base::beg); + input->Seek(-2,eCur); + input->Read(&fourCC, 4);//data + } + if(fourCC!=0x61746164){ + Log("Cant find data ident in %s\n",fileName); + g_fs.Close(input); + return false; + } + } + input->Read(&chunkSize, 4); + + if(chunkSize<0){ + Log("Bad chunk size (%d) in %s\n",chunkSize,fileName); + g_fs.Close(input); + return false; + } + + length = chunkSize; + buffer = new char[length]; + input->Read(buffer, length); + + g_fs.Close(input); + + return true; +} + +float AudioClip::GetSample(int i, int c) +{ + if(width==16) + return ((short*)buffer)[i*channels+c]/65535.0f; + else + return -0.5+((uint8_t*)buffer)[i*channels+c]/255.0f; +} + +/* +bool AudioClip::LoadFromFile(const char *fileName) +{ + string filePath = "sound/"+string(fileName); + char path[256]; + GetFilePath(filePath.c_str(), path); + ifstream input(path, ios::binary); + if(!input) + { + //LOG( "File: %s not found\n", path); + return false; + } + + int fourCC; + int chunkSize; + int fmt=0; + int width=0; + input.read((char*)&fourCC, 4);//RIFF + input.read((char*)&chunkSize, 4); + //LOG("%s chunk size %d\n",(char*)&fourCC, chunkSize); + input.read((char*)&fourCC, 4);//WAVE + input.read((char*)&fourCC, 4);//"fmt " + input.read((char*)&chunkSize, 4); + //LOG("%s chunk size %d\n",(char*)&fourCC, chunkSize); + input.read((char*)&fmt, 2); + input.read((char*)&channels, 2); + input.read((char*)&freq, 4); + input.seekg(6,ios_base::cur); + input.read((char*)&width, 2); + //LOG("fmt %d, channels %d, freq %d, width %d\n", fmt, channels, freq, width); + input.read((char*)&fourCC, 4);//data + input.read((char*)&chunkSize, 4); + //LOG("%s chunk size %d\n",(char*)&fourCC, chunkSize); + + length = chunkSize; + buffer = new char[length]; + input.read((char*)buffer, length); + + input.close(); + + LOG("Loaded sound %s (size %d)\n",fileName,length); + return true; +} +*/ diff --git a/src/sound/AudioClip.h b/src/sound/AudioClip.h new file mode 100644 index 0000000..7f64a64 --- /dev/null +++ b/src/sound/AudioClip.h @@ -0,0 +1,18 @@ + +#pragma once + +class AudioClip +{ +public: + void *buffer; + unsigned int length; + int freq; + int channels; + int width; + AudioClip(); + ~AudioClip(); + + //TODO: move to wav loader + bool LoadFromFile(const char *fileName); + float GetSample(int i, int c=0); +}; diff --git a/src/sound/sound.h b/src/sound/sound.h new file mode 100644 index 0000000..37dccbc --- /dev/null +++ b/src/sound/sound.h @@ -0,0 +1,48 @@ + +#pragma once + +#include +#include +#include + +class AudioClip; +class ISoundSystem; + +ISoundSystem *CreateSoundSystem(); + +#define SOUND_3D 1 +#define SOUND_QUEUE 2 +//#defone SOUND_MUSIC 4 + +class IAudioSource{ +public: + virtual ~IAudioSource(){} + virtual void SetPos(glm::vec3 pos)=0; + virtual void Play(bool loop=false)=0; + virtual void Stop()=0; +}; + +class ISoundSystem +{ +public: + virtual ~ISoundSystem(){} + virtual void Init(int flags=0)=0; + virtual void Update(float deltaTime)=0; + virtual void Destroy()=0; + + virtual IAudioSource *CreateSource(const char *name)=0; + virtual IAudioSource *CreateSource(AudioClip *ac)=0; + virtual void SetListenerOrientation(glm::mat4 mtx)=0; +}; + +/* + void InitQueuePlayer(); + void Init3DPlayer(); + void Play(AudioClip *clip); + void Play3D(AudioClip *clip); + void StopSound(); + void PlayMusic(const char *path); + void StopMusic(); + +}; +*/ diff --git a/src/sound/sound_al.cpp b/src/sound/sound_al.cpp new file mode 100644 index 0000000..02f9fad --- /dev/null +++ b/src/sound/sound_al.cpp @@ -0,0 +1,272 @@ + +#include +#include +#include +#include +#include + +#include +#include + +#include "log.h" +#include "system/FileSystem.h" +#include "sound/sound.h" +#include "sound/AudioClip.h" + +using namespace std; + +int CheckALError(const char *func, const char* file, int line); + +class ALSource: public IAudioSource{ +public: + ALSource(AudioClip *ac); + ~ALSource(); + void SetPos(glm::vec3 pos); + void Play(bool loop=false); + void Stop(); + +private: + ALuint buffer; + ALuint source; +}; + +class ALSoundSystem: public ISoundSystem{ +public: + ALSoundSystem():sources(),device(0),context(0){} + ~ALSoundSystem(){} + void Init(int flags=0); + void Update(float deltaTime); + void Destroy(); + + IAudioSource *CreateSource(const char *name); + IAudioSource *CreateSource(AudioClip *ac); + void SetListenerOrientation(glm::mat4 mtx); + +private: + std::vector sources; + + ALCdevice *device; + ALCcontext *context; +}; + +ISoundSystem *CreateSoundSystem(){ + return new ALSoundSystem(); +} + + +void ALSoundSystem::Init(int flags) +{ + Log("InitSound\n"); + const char *selectedDeviceName = NULL; + + if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")){ + const char *allDevices = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + int num=0; + const char *curDevice = allDevices; + //Log("OpenAL Devices:\n"); + while(*curDevice){ + //Log("AL Device %d: %s\n",num,curDevice); + if(!selectedDeviceName&&strstr(curDevice,"OpenAL Soft")){ + selectedDeviceName = curDevice; + } + num++; + curDevice += strlen(curDevice)+1; + } + } + + device = alcOpenDevice(selectedDeviceName); + + if(device){ + context = alcCreateContext(device, 0); + alcMakeContextCurrent(context); + }else{ + Log("AL: no device\n"); + return; + } + + CheckALError("Init", __FILE__, __LINE__); + + Log("OpenAL %s %s %s (%s)\n",alGetString(AL_VERSION),alGetString(AL_VENDOR),alGetString(AL_RENDERER),alcGetString(device, ALC_DEVICE_SPECIFIER)); + //Log("Extensions: %s\n",alGetString(AL_EXTENSIONS)); + + +} + +void ALSoundSystem::SetListenerOrientation(glm::mat4 mtx) +{ + alListenerfv(AL_POSITION, &(mtx[3].x)); + //alListenerfv(AL_VELOCITY, listenerVel); + float listenerOri[]={mtx[2].x,mtx[2].y,mtx[2].z, mtx[1].x,mtx[1].y,mtx[1].z};//fwd up + alListenerfv(AL_ORIENTATION, listenerOri); +} + +void ALSoundSystem::Update(float deltaTime){ + +} + +void ALSoundSystem::Destroy() +{ + //StopMusic(); + + for(uint32_t i=0; iwidth==16){ + if(ac->channels==2) + format = AL_FORMAT_STEREO16; + else + format = AL_FORMAT_MONO16; + }else{ + if(ac->channels==2) + format = AL_FORMAT_STEREO8; + else + format = AL_FORMAT_MONO8; + } + + alGenBuffers(1, &buffer); + alBufferData(buffer, format, ac->buffer, ac->length, ac->freq); + + alGenSources(1, &source); + alSourcei(source, AL_BUFFER, buffer); +} + +ALSource::~ALSource() +{ + alDeleteSources(1, &source); + alDeleteBuffers(1, &buffer); +} + +void ALSource::SetPos(glm::vec3 pos) +{ + alSourcefv(source, AL_POSITION, &(pos.x)); +} + +void ALSource::Play(bool loop) +{ + alSourcei(source, AL_LOOPING, loop); + alSourcePlay(source); +} + +void ALSource::Stop() +{ + alSourceStop(source); +} + +const char *GetALErrorString(int err) +{ + switch(err){ + case AL_INVALID_NAME: + return "AL_INVALID_NAME"; + case AL_INVALID_ENUM: + return "AL_INVALID_ENUM"; + case AL_INVALID_VALUE: + return "AL_INVALID_VALUE"; + case AL_INVALID_OPERATION: + return "AL_INVALID_OPERATION"; + case AL_OUT_OF_MEMORY: + return "AL_OUT_OF_MEMORY"; + default: + return "???"; + } +} + +int CheckALError(const char *func, const char* file, int line) +{ + ALenum err = alGetError(); + if(err){ + Log("al Error 0x%X(%s) on %s %s(%d)\n",err,GetALErrorString(err),func,file,line); + } + return err; +} + +// +#if 0 +void ALSoundSystem::InitQueuePlayer() +{ + +} + +void ALSoundSystem::Play(AudioClip *clip) +{ + int format = 0; + if(clip->width==16){ + if(clip->channels==2) + format = AL_FORMAT_STEREO16; + else + format = AL_FORMAT_MONO16; + }else{ + if(clip->channels==2) + format = AL_FORMAT_STEREO8; + else + format = AL_FORMAT_MONO8; + } + + alSourceStop(source); + CheckALError("alSourceStop", __FILE__, __LINE__); + alSourcei(source,AL_BUFFER,0); + CheckALError("alSourcei 0", __FILE__, __LINE__); + alBufferData(buffer, format, clip->buffer, clip->length, clip->freq); + CheckALError("alBufferData", __FILE__, __LINE__); + alSourcei(source, AL_BUFFER, buffer); + CheckALError("alSourcei buffer", __FILE__, __LINE__); + alSourcePlay(source); + CheckALError("alSourcePlay", __FILE__, __LINE__); +} + +void ALSoundSystem::Play3D(AudioClip *clip) +{ + Log("SoundSystem::Play3D(%p)\n",clip); +} + +void ALSoundSystem::StopSound() +{ + alSourceStop(source); + CheckALError("alSourceStop", __FILE__, __LINE__); +} + +void ALSoundSystem::PlayMusic(const char *path) +{ + Log("PlayMusic %s\n", path); + if(!std::ifstream(path)) + Log("File not found!\n"); + + //nothing +} + +void ALSoundSystem::StopMusic() +{ + alSourceStop(source); +} +#endif diff --git a/src/sound/sound_sles.cpp b/src/sound/sound_sles.cpp new file mode 100644 index 0000000..d9e3ff1 --- /dev/null +++ b/src/sound/sound_sles.cpp @@ -0,0 +1,322 @@ + +#include +#include + +#include + +#include "log.h" +#include "sound.h" +#include "system/FileSystem.h" +#include "sound/AudioClip.h" + +using namespace std; + +SLObjectItf engineObj=0; +SLEngineItf engine=0; +SLObjectItf outputMixObj=0; + +SLObjectItf bgPlayerObj=0; +SLPlayItf bgPlayer=0; + +SLObjectItf queuePlayerObj=0; +SLPlayItf queuePlayer=0; +SLBufferQueueItf bufferQueue=0; + +SLObjectItf listener=0; +SL3DLocationItf listenerLoc=0; + +SLObjectItf player3DObj=0; +SLPlayItf player3D=0; +SL3DLocationItf player3DLoc=0; +SLBufferQueueItf bufferQueue3D=0; + +const char *StringFromSLRes(SLresult res){ + if(res==SL_RESULT_SUCCESS) + return "SL_RESULT_SUCCESS"; + if(res==SL_RESULT_PARAMETER_INVALID) + return "SL_RESULT_PARAMETER_INVALID"; + if(res==SL_RESULT_FEATURE_UNSUPPORTED) + return "SL_RESULT_FEATURE_UNSUPPORTED"; + return 0; +} + +bool CheckSLError(SLresult res, const char *func, const char* file, int line){ + if(res){ + Log("sles Error %s(0x%lX) on %s %s(%d)\n",StringFromSLRes(res),res,func,file,line); + } + return res; +} + +void SoundSystem::Init(int flags) +{ + Log("InitSound\n"); + + SLresult res; + + /*SLuint32 numItfs=0; + res = slQueryNumSupportedEngineInterfaces(&numItfs); + Log("NumSupportedEngineInterfaces %d\n",numItfs); + for(SLuint32 i=0;iRealize(engineObj, SL_BOOLEAN_FALSE); + CheckSLError(res,"sl engineObj Realize", __FILE__, __LINE__); + res = (*engineObj)->GetInterface(engineObj, SL_IID_ENGINE, &engine); + CheckSLError(res,"sl engineObj GetInterface engine", __FILE__, __LINE__); + /*SLuint32 numExts; + res = (*engine)->QueryNumSupportedExtensions(engine,&numExts); + CheckSLError(res,"sl QueryNumSupportedExtensions", __FILE__, __LINE__); + Log("sl num extensions: %d\n",numExts); + for(SLuint32 i=0;iQuerySupportedExtension(engine,i,(SLchar*)extName,&extNameLen); + CheckSLError(res,"sl QuerySupportedExtension", __FILE__, __LINE__); + Log("ext %d: %s\n",i,extName); + }*/ + /*SLEngineCapabilitiesItf engCaps; + res = (*engineObj)->GetInterface(engineObj, SL_IID_ENGINECAPABILITIES, &engCaps); + if(!CheckSLError(res,"sl engineObj GetItf engCaps", __FILE__, __LINE__)){ + SLint16 ver[3]; + res = (*engCaps)->QueryAPIVersion(engCaps, ver, ver+1, ver+2); + CheckSLError(res,"sl QueryAPIVersion", __FILE__, __LINE__); + SLuint16 profilesSupported = 0; + res = (*engCaps)->QuerySupportedProfiles(engCaps, &profilesSupported); + CheckSLError(res,"sl QuerySupportedProfiles", __FILE__, __LINE__); + Log("OpenSL ES ver(%d.%d.%d), supported profiles %d\n",ver[0],ver[1],ver[2],profilesSupported); + }*/ + + res = (*engine)->CreateOutputMix(engine, &outputMixObj, 0, 0, 0); + CheckSLError(res,"sl engine CreateOutputMix outputMixObj", __FILE__, __LINE__); + res = (*outputMixObj)->Realize(outputMixObj, SL_BOOLEAN_FALSE); + CheckSLError(res,"sl outputMixObj Realize", __FILE__, __LINE__); + + if(flags&SOUND_QUEUE){ + InitQueuePlayer(); + } + + if(flags&SOUND_3D){ + Init3DPlayer(); + } + + //Log("Done\n"); +} + +void SoundSystem::Init3DPlayer(){ + Log("Sound: Init Listener\n"); + + SLresult res; + + const SLInterfaceID listenerItfs[]={SL_IID_3DLOCATION}; + const SLboolean listenerReqs[]={SL_BOOLEAN_TRUE}; + res = (*engine)->CreateListener(engine,&listener,1,listenerItfs,listenerReqs); + CheckSLError(res,"CreateListener", __FILE__, __LINE__); + res = (*listener)->Realize(listener,SL_BOOLEAN_FALSE); + CheckSLError(res,"listener Realize", __FILE__, __LINE__); + res = (*listener)->GetInterface(listener, SL_IID_3DLOCATION, &listenerLoc); + CheckSLError(res,"sl player3DObj GetInterface Location", __FILE__, __LINE__); + + Log("Sound: Init 3D Player\n"); + + //SLDataSource dataSrc={0,0}; + SLDataLocator_BufferQueue dataLocIn={SL_DATALOCATOR_BUFFERQUEUE, 1}; + SLDataFormat_PCM dataFormat; + dataFormat.formatType = SL_DATAFORMAT_PCM; + dataFormat.numChannels = 1;//mono + dataFormat.samplesPerSec = SL_SAMPLINGRATE_44_1; + dataFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + dataFormat.containerSize = 16; + dataFormat.channelMask = SL_SPEAKER_FRONT_CENTER; + dataFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; + SLDataSource dataSrc={&dataLocIn, &dataFormat}; + + SLDataLocator_OutputMix dataLocOut={SL_DATALOCATOR_OUTPUTMIX, outputMixObj}; + SLDataSink dataSink={&dataLocOut,0}; + + const SLInterfaceID playerIIds[]={SL_IID_PLAY,SL_IID_3DLOCATION,SL_IID_BUFFERQUEUE}; + const SLboolean playerReqs[]={SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE}; + int numReqs=3; + + res = (*engine)->CreateAudioPlayer(engine, &player3DObj, &dataSrc, &dataSink, numReqs, playerIIds, playerReqs); + if(CheckSLError(res,"sl CreateAudioPlayer Player3DObj", __FILE__, __LINE__)) + return; + res = (*player3DObj)->Realize(player3DObj, SL_BOOLEAN_FALSE); + CheckSLError(res,"sl player3DObj Realize", __FILE__, __LINE__); + res = (*player3DObj)->GetInterface(player3DObj, SL_IID_PLAY, &player3D); + CheckSLError(res,"sl player3DObj GetInterface Play", __FILE__, __LINE__); + res = (*player3DObj)->GetInterface(player3DObj, SL_IID_3DLOCATION, &player3DLoc); + CheckSLError(res,"sl player3DObj GetInterface Location", __FILE__, __LINE__); + res = (*player3DObj)->GetInterface(player3DObj, SL_IID_BUFFERQUEUE, &bufferQueue3D); + CheckSLError(res,"sl player3DObj GetInterface bufferQueue3D", __FILE__, __LINE__); + +} + +void SoundSystem::InitQueuePlayer() +{ + //SLDataLocator_AndroidSimpleBufferQueue + SLDataLocator_BufferQueue dataLocIn={SL_DATALOCATOR_BUFFERQUEUE, 1}; + SLDataFormat_PCM dataFormat; + dataFormat.formatType = SL_DATAFORMAT_PCM; + //dataFormat.numChannels = 1;//mono + dataFormat.numChannels = 2; + dataFormat.samplesPerSec = SL_SAMPLINGRATE_44_1; + dataFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + dataFormat.containerSize = 16; + //dataFormat.channelMask = SL_SPEAKER_FRONT_CENTER; + dataFormat.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + dataFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; + SLDataSource dataSrc={&dataLocIn, &dataFormat}; + + SLDataLocator_OutputMix dataLocOut={SL_DATALOCATOR_OUTPUTMIX, outputMixObj}; + SLDataSink dataSink={&dataLocOut,0}; + + const SLInterfaceID playerIIds[]={SL_IID_PLAY, SL_IID_BUFFERQUEUE}; + const SLboolean playerReqs[]={SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + + SLresult res; + + res = (*engine)->CreateAudioPlayer(engine, &queuePlayerObj, &dataSrc, &dataSink, 2, playerIIds, playerReqs); + CheckSLError(res,"sl engine CreateAudioPlayer queuePlayerObj", __FILE__, __LINE__); + if(res) + return; + res = (*queuePlayerObj)->Realize(queuePlayerObj, SL_BOOLEAN_FALSE); + CheckSLError(res,"sl queuePlayerObj Realize", __FILE__, __LINE__); + res = (*queuePlayerObj)->GetInterface(queuePlayerObj, SL_IID_PLAY, &queuePlayer); + CheckSLError(res,"sl queuePlayerObj GetInterface Play", __FILE__, __LINE__); + res = (*queuePlayerObj)->GetInterface(queuePlayerObj, SL_IID_BUFFERQUEUE, &bufferQueue); + CheckSLError(res,"sl queuePlayerObj GetInterface bufferQueue", __FILE__, __LINE__); + + res = (*queuePlayer)->SetPlayState(queuePlayer, SL_PLAYSTATE_PLAYING); + CheckSLError(res,"sl queuePlayer SetPlayState SL_PLAYSTATE_PLAYING", __FILE__, __LINE__); +} + +void SoundSystem::Play(AudioClip *clip) +{ + SLresult res; + SLuint32 playerState; + + if(!queuePlayerObj) + return; + + res = (*queuePlayerObj)->GetState(queuePlayerObj, &playerState); + //Log("sl queuePlayerObj GetState playerState %lx\n", res); + if(playerState == SL_OBJECT_STATE_REALIZED) + { + res = (*bufferQueue)->Clear(bufferQueue); + //Log("sl bufferQueue Clear %lx\n", res); + + res = (*bufferQueue)->Enqueue(bufferQueue, clip->buffer, clip->length); + //Log("sl bufferQueue Enqueue %lx\n", res); + + res = (*queuePlayer)->SetPlayState(queuePlayer, SL_PLAYSTATE_PLAYING); + + CheckSLError(res,"SoundSystem::Play", __FILE__, __LINE__); + } + else + Log("sl playerState %lx\n", playerState); +} + +void SoundSystem::Play3D(AudioClip *clip) +{ + SLresult res; + SLuint32 playerState; + + if(!player3DObj) + return; + + res = (*player3DObj)->GetState(player3DObj, &playerState); + if(playerState == SL_OBJECT_STATE_REALIZED){ + res = (*bufferQueue3D)->Clear(bufferQueue3D); + res = (*bufferQueue3D)->Enqueue(bufferQueue3D, clip->buffer, clip->length); + res = (*player3D)->SetPlayState(player3D, SL_PLAYSTATE_PLAYING); + + CheckSLError(res,"SoundSystem::Play3D", __FILE__, __LINE__); + } + else + Log("sl Play3D playerState %lx\n", playerState); +} + +void SoundSystem::StopSound() +{ + SLresult res = (*queuePlayer)->SetPlayState(queuePlayer, SL_PLAYSTATE_STOPPED); + CheckSLError(res,"sl queuePlayer SetPlayState SL_PLAYSTATE_STOPPED", __FILE__, __LINE__); +} + +SLchar uri[1024]={0}; +void SoundSystem::PlayMusic(const char *path) +{ + Log("PlayMusic %s\n", path); + if(!std::ifstream(path)) + Log("File not found!\n"); + snprintf((char*)uri,256,"file://%s",path); + + SLDataLocator_URI dataLocIn={SL_DATALOCATOR_URI, uri}; + SLDataFormat_MIME dataFormat={SL_DATAFORMAT_MIME, 0, SL_CONTAINERTYPE_UNSPECIFIED}; + SLDataSource dataSrc={&dataLocIn, &dataFormat}; + + SLDataLocator_OutputMix dataLocOut={SL_DATALOCATOR_OUTPUTMIX, outputMixObj}; + SLDataSink dataSink={&dataLocOut,0}; + + const SLInterfaceID playerIIds[]={SL_IID_PLAY}; + const SLboolean playerReqs[]={SL_BOOLEAN_TRUE}; + + SLresult res; + + res = (*engine)->CreateAudioPlayer(engine, &bgPlayerObj, &dataSrc, &dataSink, 1, playerIIds, playerReqs); + CheckSLError(res,"sl engine CreateAudioPlayer bgPlayerObj", __FILE__, __LINE__); + res = (*bgPlayerObj)->Realize(bgPlayerObj, SL_BOOLEAN_FALSE); + CheckSLError(res,"sl bgPlayerObj Realize", __FILE__, __LINE__); + res = (*bgPlayerObj)->GetInterface(bgPlayerObj, SL_IID_PLAY, &bgPlayer); + CheckSLError(res,"sl bgPlayerObj GetInterface bgPlayer", __FILE__, __LINE__); + + res = (*bgPlayer)->SetPlayState(bgPlayer, SL_PLAYSTATE_PLAYING); + CheckSLError(res,"sl bgPlayer SetPlayState SL_PLAYSTATE_PLAYING", __FILE__, __LINE__); +} + +void SoundSystem::StopMusic() +{ + if(bgPlayer){ + SLresult res; + SLuint32 playerState; + res = (*bgPlayerObj)->GetState(bgPlayerObj, &playerState); + CheckSLError(res,"sl bgPlayerObj GetState playerState", __FILE__, __LINE__); + Log("sl playerState %lx\n", playerState); + if(playerState == SL_OBJECT_STATE_REALIZED){ + res = (*bgPlayer)->SetPlayState(bgPlayer, SL_PLAYSTATE_PAUSED); + CheckSLError(res,"sl bgPlayer SetPlayState SL_PLAYSTATE_PAUSED", __FILE__, __LINE__); + + (*bgPlayerObj)->Destroy(bgPlayerObj); + + bgPlayerObj = 0; + bgPlayer = 0; + } + } +} + +void SoundSystem::Destroy() +{ + StopMusic(); + + if(queuePlayerObj){ + (*queuePlayerObj)->Destroy(queuePlayerObj); + queuePlayerObj=0; + queuePlayer=0; + bufferQueue=0; + } + if(outputMixObj){ + (*outputMixObj)->Destroy(outputMixObj); + outputMixObj=0; + } + if(engineObj){ + (*engineObj)->Destroy(engineObj); + engineObj=0; + engine=0; + } +} diff --git a/src/system/FileSystem.cpp b/src/system/FileSystem.cpp new file mode 100644 index 0000000..056fd44 --- /dev/null +++ b/src/system/FileSystem.cpp @@ -0,0 +1,409 @@ + +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif + +#include "system/FileSystem.h" +#include "log.h" + +using namespace std; + +FileSystem g_fs; +ofstream fslog; + +void replace(char *str, char a, char b){ + for(int i=0; str[i]; i++){ + if(str[i]==a) + str[i]=b; + } +} + +FileSystem::~FileSystem() +{ + +} + +bool FileSystem::Init(const char *dir) +{ + arc = 0; + gamedir = dir; + + char path[256]; +#ifdef WIN32 + snprintf(path,256,".\\%s", dir); +#else + snprintf(path,256,"%s/nenuzhno-engine/%s", getenv("EXTERNAL_STORAGE"), dir); +#endif + Log("InitFS() gamedir = %s\n",path); + + if(!DirExists(path)){ + Log("FS: gamedir doesn't exist\n"); +#ifdef WIN32 + if(int e=_mkdir(path)){ + Log("FS: Can't create gamedir (%d)\n",e); + } +#else + mkdir(path,0); +#endif + return false; + } + + LoadSearchPaths(); +#ifdef WIN32 + snprintf(path,256,"%s/fslog.txt", dir); +#else + snprintf(path,256,"%s/nenuzhno-engine/%s/fslog.txt", getenv("EXTERNAL_STORAGE"), dir); +#endif + fslog.open(path); + if(!fslog){ + Log("FS: Can't create fs log file!\n"); + return false; + } + return true; +} + +IFile *FileSystem::Open(const char *name) +{ + if(arc){ + if(arc->FileExists(name)){ + IFile *f = arc->Open(name); + if(f) + return f; + } + } + neFile *f = new neFile(); + if(f->Open(name)) + return f; + delete f; + + return 0; +} + +void FileSystem::Close(IFile *f) +{ + if(f->flags&FILE_ZIP_ENTRY) + return; + + delete f; +} + +char* FileSystem::ReadAll(const char *fileName){ + IFile *f = Open(fileName); + if(!f) + return NULL; + + char* content = new char[ f->GetLen()+1 ]; + + f->Read(content, f->GetLen()); + content[f->GetLen()] = '\0'; + /*if(f->flags&FILE_ZIP_ENTRY){ + Log("FileSystem::ReadAll(%s) len %d\n",fileName,f->GetLen()); + //Log("content: %s\n",content); + }*/ + Close(f); + + return content; +} + +bool FileSystem::WriteAll(const char *fileName, const char *data){ + char path[1024]; + g_fs.GetFilePath(fileName,path); + ofstream out(path); + out.write(data,strlen(data)); + out.close(); + return true; +} + +bool FileSystem::FileExists(const char *fileName, bool gamedirOnly) +{ + char path[256]; +#ifdef WIN32 + snprintf(path,256,"%s/%s", gamedir.c_str(), fileName); +#else + snprintf(path,256,"%s/nenuzhno-engine/%s/%s", getenv("EXTERNAL_STORAGE"), gamedir.c_str(), fileName); +#endif + if(ifstream(path)){ + return true; + } + if(gamedirOnly) + return false; + for(int i = 0; i> numSearchPaths; + searchPaths.Resize(numSearchPaths); + for(int i = 0; i < numSearchPaths; i++){ + input >> temp; + searchPaths[i] = temp; + } + input.close(); + Log("FS: Loaded %d search paths\n",numSearchPaths); + } +} + +neFile::~neFile(){ + strm.close(); +} + +bool neFile::Open(const char *name) +{ + char path[1024]; + g_fs.GetFilePath(name,path); + strm.open(path,ios::binary); + if(strm){ + strm.seekg(0, ios::end); + len = (int)strm.tellg(); + strm.seekg(0); + return true; + } + return false; +} + +int neFile::GetLen() +{ + return len; +} + +int neFile::GetPos() +{ + return (int)strm.tellg(); +} + +bool neFile::eof() +{ + return strm.eof(); +} + +void neFile::Seek(int ofs, eOrig_t orig) +{ + ios_base::seekdir dir = ios_base::beg; + if(orig==eCur) + dir = ios_base::cur; + else if(orig==eEnd) + dir = ios_base::end; + + strm.seekg(ofs,dir); +} + +void neFile::Read(void *d, int l) +{ + strm.read((char*)d,l); +} + +void neFile::GetLine(string &str) +{ + getline(strm,str); +} + +void neFile::GetString(char *s, int l) +{ + if(l>len-(int)strm.tellg()) + l = len-(int)strm.tellg(); + strm.getline(s,l,'\0'); +} + + +#pragma pack(2) +struct zipEntryHeader_t{ + uint32_t signature; + uint16_t version; + uint16_t flags; + uint16_t compression; + uint16_t last_mod_file_time; + uint16_t last_mod_file_date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_length; + uint16_t extra_field_length; +}; +#pragma pack() + + +#include "neZip.h" +zipArchive::zipArchive(){ + data = 0; + len = 0; + //Log("zipArchive()\n"); +} +zipArchive::zipArchive(char *d, int l){ + //Log("zipArchive(%p, %d)\n",d,l); + data = d; + len = l; + + zipEntryHeader_t *entry; + char entryName[1024]; + char *curOffs = data; + int n = 0; + while(true){ + entry = (zipEntryHeader_t*)curOffs; + if(entry->signature!=0x04034b50){ + Log("zip: signature %X\n",entry->signature); + break; + } + curOffs += sizeof(zipEntryHeader_t); + strncpy(entryName,curOffs,entry->file_name_length); + entryName[entry->file_name_length] = 0; + curOffs += entry->file_name_length; + curOffs += entry->extra_field_length; + //Log("entry %d: %s, size %d, ver %d\n",n,entryName,entry->uncompressed_size,entry->version); + + entries[entryName] = zipEntry(curOffs,entry->uncompressed_size); + + curOffs += entry->uncompressed_size; + n++; + if((curOffs-data) >= len){ + Log("zip: eof\n"); + break; + } + } + Log("Loaded %d zip files\n",n); +} + +zipArchive::~zipArchive() +{ + //Log("~zipArchive() data %p len %d\n",data,len); + //delete[] data; +} + +bool zipArchive::FileExists(const char *name) +{ + return (entries.find(std::string(name))!=entries.end()); +} + +IFile *zipArchive::Open(const char *name) +{ + std::string temp(name); + if(entries.find(temp)!=entries.end()) + return &(entries[temp]); + + /* + for(uint32_t i=0;i=l) + t = l-1; + strncpy(s,data+p,t); + data[t] = 0; +} diff --git a/src/system/FileSystem.h b/src/system/FileSystem.h new file mode 100644 index 0000000..c320792 --- /dev/null +++ b/src/system/FileSystem.h @@ -0,0 +1,40 @@ + +#pragma once + +#include +#include +#include "system/neFile.h" +#include "system/neArray.h" + +//bool GetFilePath(const char *fileName, char *path, bool gamedirOnly = false); + +class IArchive{ +public: + virtual ~IArchive(){} + virtual bool FileExists(const char *name)=0; + virtual IFile *Open(const char *name)=0; +}; + +class FileSystem{ +public: + ~FileSystem(); + bool Init(const char *dir); + IFile *Open(const char *name); + void Close(IFile *n); + char *ReadAll(const char *name); + bool WriteAll(const char *fileName, const char *data); + bool FileExists(const char *name,bool gamedirOnly=false); + bool DirExists(const char *name); + + void AddArchive(IArchive *archive); + + bool GetFilePath(const char *fileName, char *path, bool gamedirOnly = false); + + std::string gamedir; +private: + void LoadSearchPaths(); + neArray searchPaths; + IArchive *arc; +}; + +extern FileSystem g_fs; diff --git a/src/system/config.cpp b/src/system/config.cpp new file mode 100644 index 0000000..5fa5aba --- /dev/null +++ b/src/system/config.cpp @@ -0,0 +1,77 @@ + +#include "config.h" +#include +#include +#include +using namespace std; +#include "log.h" +#include "system/FileSystem.h" + +ConfigFile::ConfigFile():values(){ + +} + +ConfigFile::~ConfigFile(){ + +} + +bool ConfigFile::Load(const char *fileName){ + IFile *cfg = g_fs.Open(fileName); + if(!cfg){ + Log("Config file not found!\n"); + return false; + } + + string line; + while(!cfg->eof()) + { + cfg->GetLine(line); + //Log("line (%s)\n",line.c_str()); + + if(line.empty()) + continue; + if(line[0]=='#') + continue; + if(line.find("//")==0) + continue; + if(line[0]==' '){ + Log("Error: config line (%s) has no key\n",line.c_str()); + continue; + } + + uint32_t t = line.find(' '); + string key = line.substr(0,t); + //Log("key: (%s)\n",key.c_str()); + if(t==string::npos|| line.length()-t<2){ + Log("Error: key(%s) has no value\n",key.c_str()); + continue; + } + + string val=line.substr(t+1,line.length()-t-1); + Log("%d: %s = %s\n",values.size(),key.c_str(),val.c_str()); + + values[key] = val; + } + + g_fs.Close(cfg); + return true; +} + +glm::vec3 ConfigFile::GetVec3(const char *k){ + glm::vec3 out; + sscanf(values[k].c_str(), "%f %f %f", &out.x, &out.y, &out.z); + return out; +} + +int ConfigFile::GetInt(const char *k){ + int out = 0; + sscanf(values[k].c_str(), "%d", &out); + return out; +} + +float ConfigFile::GetFloat(const char *k){ + float out = 0; + sscanf(values[k].c_str(), "%f", &out); + return out; +} + diff --git a/src/system/config.h b/src/system/config.h new file mode 100644 index 0000000..320e04c --- /dev/null +++ b/src/system/config.h @@ -0,0 +1,21 @@ + +#pragma once + +#include +#include +#include + +class ConfigFile{ +public: + ConfigFile(); + ~ConfigFile(); + bool Load(const char* fileName); + std::string& operator[] (const char *k){ + return values[k]; + } + glm::vec3 GetVec3(const char *k); + int GetInt(const char *k); + float GetFloat(const char *k); + std::map values; +}; + diff --git a/src/system/neArray.h b/src/system/neArray.h new file mode 100644 index 0000000..e257150 --- /dev/null +++ b/src/system/neArray.h @@ -0,0 +1,41 @@ + +#pragma once + +template +struct neArray{ + neArray(){ + size = 0; + data = 0; + } + neArray(int n){ + size = n; + data = new T[n]; + } + ~neArray(){ + if(data&&size) + delete[] data; + } + T& operator[] (int n){ + return data[n]; + } + void Resize(int n){ + if(data) + delete[] data; + data = new T[n]; + size = n; + } + void Clear(){ + if(data&&size){ + delete[] data; + data = 0; + size = 0; + } + } + + int size; + T *data; + +private: + neArray(const neArray &old); + neArray& operator= (const neArray &na); +}; diff --git a/src/system/neFile.h b/src/system/neFile.h new file mode 100644 index 0000000..7175c8c --- /dev/null +++ b/src/system/neFile.h @@ -0,0 +1,45 @@ + +#pragma once + +#include + +enum eOrig_t{ + eBeg, + eCur, + eEnd +}; + +#define FILE_ZIP_ENTRY 32 + +class IFile{ +public: + virtual ~IFile(){} + + virtual int GetLen()=0; + virtual int GetPos()=0; + virtual bool eof()=0; + virtual void Seek(int ofs, eOrig_t orig=eBeg)=0; + virtual void Read(void *d,int l)=0; + virtual void GetString(char *s, int l)=0; + virtual void GetLine(std::string &str)=0; + + int flags; +}; + +class neFile: public IFile{ +public: + neFile(){flags=0;len=0;} + ~neFile(); + + bool Open(const char *name); + int GetLen(); + int GetPos(); + bool eof(); + void Seek(int ofs, eOrig_t orig); + void Read(void *d, int l); + void GetString(char *s, int l); + void GetLine(std::string &str); +private: + std::ifstream strm; + int len; +}; diff --git a/src/system/neZip.h b/src/system/neZip.h new file mode 100644 index 0000000..9f5a757 --- /dev/null +++ b/src/system/neZip.h @@ -0,0 +1,39 @@ + +#pragma once + +#include +#include "neFile.h" +#include "system/FileSystem.h" + +class zipEntry: public IFile{ +public: + zipEntry(); + zipEntry(char *d, int l); + ~zipEntry(){} + int GetLen(){return size;} + int GetPos(){return p;} + bool eof(){ return (p>=size);} + void Seek(int ofs, eOrig_t orig); + + void Read(void *d,int l); + void GetLine(std::string &str); + void GetString(char *s, int l); + + int p; + char *data; + int size; +}; + +//in FileSystem.cpp +class zipArchive: public IArchive{ +public: + zipArchive(); + zipArchive(char *d, int l); + ~zipArchive(); + bool FileExists(const char *name); + IFile *Open(const char *name); +//private: + std::map entries; + char *data; + int len; +};