diff --git a/.gitignore b/.gitignore index ba077a4..8368f23 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bin +src/client/ccfs.* diff --git a/SConstruct b/SConstruct index 1b262c8..d4033ce 100755 --- a/SConstruct +++ b/SConstruct @@ -1,10 +1,14 @@ # vim:filetype=python import os +import re +from subprocess import Popen, PIPE client_name = 'treacherous-terrain' server_name = client_name + '-server' +CCFS_ROOT = 'assets/fs' + # determine our build platform platform = 'windows' if os.path.exists('/bin/cygwin1.dll') else 'unix' @@ -44,7 +48,7 @@ else: # our sources sources_client = [Glob('src/common/*.cc'), Glob('src/client/*.cc')] -sources_server = [Glob('src/common/*.cc'), Glob('src/server/*.cc')] +sources_server = [Glob('src/common/*.cc'), Glob('src/server/*.cc'), 'src/client/ccfs.cc'] # create the scons environments env_client = Environment( @@ -63,6 +67,38 @@ env_server = Environment( LIBPATH = LIBPATH, LIBS = LIBS_server) +# CCFS builder + +def get_all_files(prefix): + files = [] + for ent in os.listdir(prefix): + if ent.startswith('.'): + next + full_path = '%s/%s' % (prefix, ent) + if os.path.isdir(full_path): + files += get_all_files(full_path) + else: + files.append(full_path) + return files + +def CCFS(target, source, env): + source_list = [] + for s in source: + source_fname = str(s) + source_fname = source_fname.replace(CCFS_ROOT + '/', '') + source_list.append(source_fname) + Popen(['./ccfs_gen.py', '--root', 'assets/fs', str(target[0])] + source_list).wait() + return None + +def CCFS_emitter(target, source, env): + target.append(re.sub(r'\..*$', '.h', str(target[0]))) + return target, source + +env_client.Append(BUILDERS = {'CCFS' : Builder(action = CCFS, emitter = CCFS_emitter)}) + +env_client.CCFS('src/client/ccfs.cc', get_all_files(CCFS_ROOT)) +env_client.Depends('src/client/ccfs.cc', 'ccfs_gen.py') + for lib_path in libs_to_copy: installed_libs = env_client.Install(BIN_DIR, lib_path) env_client.Depends('%s/%s' % (BIN_DIR, client_name), installed_libs) diff --git a/assets/fs/shaders/obj_f.glsl b/assets/fs/shaders/obj_f.glsl new file mode 100644 index 0000000..a548c54 --- /dev/null +++ b/assets/fs/shaders/obj_f.glsl @@ -0,0 +1,33 @@ + +uniform vec4 ambient, diffuse, specular; +uniform float shininess; + +varying vec3 pos_i; +varying vec3 normal_i; + +void main(void) +{ + vec3 n, lightDir; + vec4 color; + float NdotL, RdotEye; + + lightDir = normalize(vec3(-0.1, 0, -0.9)); + color = vec4(0.2, 0.2, 0.2, 1.0) * ambient; /* ambient light */ + n = normalize(normal_i); + + NdotL = max(dot(n, -lightDir), 0.0); + + if (NdotL > 0.0) + { + /* diffuse component */ + color += diffuse * NdotL; + /* specular component */ + RdotEye = dot(normalize(-pos_i), normalize(reflect(-lightDir, n))); + if (RdotEye > 0.0) + { + color += clamp(specular * pow(RdotEye, shininess), 0.0, 1.0); + } + } + + gl_FragColor = color; +} diff --git a/assets/fs/shaders/obj_tex_f.glsl b/assets/fs/shaders/obj_tex_f.glsl new file mode 100644 index 0000000..d561d66 --- /dev/null +++ b/assets/fs/shaders/obj_tex_f.glsl @@ -0,0 +1,35 @@ + +uniform vec4 ambient, specular; +uniform float shininess; +uniform sampler2D tex; + +varying vec3 pos_i; +varying vec3 normal_i; +varying vec2 tex_coord_i; + +void main(void) +{ + vec3 n, lightDir; + vec4 color; + float NdotL, RdotEye; + + lightDir = normalize(vec3(-0.1, 0, -0.9)); + color = vec4(0.2, 0.2, 0.2, 1.0) * ambient; /* ambient light */ + n = normalize(normal_i); + + NdotL = max(dot(n, -lightDir), 0.0); + + if (NdotL > 0.0) + { + /* diffuse component */ + color += texture2D(tex, tex_coord_i) * NdotL; + /* specular component */ + RdotEye = dot(normalize(-pos_i), normalize(reflect(-lightDir, n))); + if (RdotEye > 0.0) + { + color += clamp(specular * pow(RdotEye, shininess), 0.0, 1.0); + } + } + + gl_FragColor = color; +} diff --git a/assets/fs/shaders/obj_tex_v.glsl b/assets/fs/shaders/obj_tex_v.glsl new file mode 100644 index 0000000..9be4096 --- /dev/null +++ b/assets/fs/shaders/obj_tex_v.glsl @@ -0,0 +1,16 @@ + +attribute vec3 pos; +attribute vec3 normal; +attribute vec2 tex_coord; + +varying vec3 pos_i; +varying vec3 normal_i; +varying vec2 tex_coord_i; + +void main(void) +{ + gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1); + pos_i = gl_Position.xyz; + tex_coord_i = tex_coord; + normal_i = gl_NormalMatrix * normal; +} diff --git a/assets/fs/shaders/obj_v.glsl b/assets/fs/shaders/obj_v.glsl new file mode 100644 index 0000000..7d88a5f --- /dev/null +++ b/assets/fs/shaders/obj_v.glsl @@ -0,0 +1,13 @@ + +attribute vec3 pos; +attribute vec3 normal; + +varying vec3 pos_i; +varying vec3 normal_i; + +void main(void) +{ + gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1); + pos_i = gl_Position.xyz; + normal_i = gl_NormalMatrix * normal; +} diff --git a/ccfs_gen.py b/ccfs_gen.py new file mode 100755 index 0000000..e32e05a --- /dev/null +++ b/ccfs_gen.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python + +import sys +import re +import os +import getopt + +def cname(s): + c = re.sub(r'\W', '_', s) + if re.search(r'^\d', c): + c = '_' + c + return c + +def usage(prog_name, err=False): + out = sys.stderr if err else sys.stdout + out.write('Usage: %s [options] out_file.cc paths...\n' % prog_name) + out.write(' Options:\n') + out.write(' --root dir base directory in which to look up paths\n') + out.write(' --name name name of the instance object to generate (default CFS)\n') + return 2 if err else 0 + +def main(argv): + instance_name = 'CFS' + root_dir = '.' + + opts, args = getopt.getopt(argv[1:], '', ['root=', 'name=']) + for opt, val in opts: + if opt == '--root': + root_dir = val + elif opt == '--name': + instance_name = val + else: + sys.stderr.write('Unrecognized command-line option: "%s"\n' % opt) + return 2 + + if len(args) < 2: + return usage(argv[0], True) + + out_fname = args[0] + paths = args[1:] + + store = {} + header_fname = re.sub(r'\..*$', '.h', out_fname) + if header_fname == out_fname: + sys.stderr.write('Output file requires file extension\n') + return 2 + c_file = open(out_fname, 'w') + h_file = open(header_fname, 'w') + c_file.write('#include \n') + c_file.write('#include "%s"\n' % os.path.basename(header_fname)) + for p in paths: + c_name = cname(p) + c_file.write('static const unsigned char %s[] = {' % c_name) + src = open('%s/%s' % (root_dir, p), 'r') + s_len = 0 + while 1: + if s_len % 12 == 0: + c_file.write('\n ') + ch = src.read(1) + if len(ch) < 1: + break + s_len += 1 + c_file.write('0x%02x, ' % ord(ch)) + c_file.write('0x00\n') + src.close() + c_file.write('};\n') + store[p] = (c_name, s_len) + c_file.write('''static const struct { + const char *fname; + const unsigned char *data; + const unsigned int len; +} store[] = {\n''') + for ent in store: + c_file.write(' {"%s", %s, %d},\n' % \ + (ent, store[ent][0], store[ent][1])) + c_file.write(' {NULL, NULL, 0}\n') + c_file.write('};\n') + + c_file.write(''' +const unsigned char *CCFSClass::get_file(const char *fname, unsigned int *length) +{ + int i; + for (i = 0; store[i].fname != NULL; i++) + { + if (strcmp(fname, store[i].fname) == 0) + { + if (length != NULL) + *length = store[i].len; + return store[i].data; + } + } + return NULL; +} +''') + h_file.write('''#ifndef CCFS_GEN_%s +#define CCFS_GEN_%s + +class CCFSClass +{ +public: + const unsigned char *get_file(const char *fname, unsigned int *length); +}; + +CCFSClass %s; + +#endif +''' % (cname(header_fname), cname(header_fname), instance_name)) + h_file.close() + c_file.close() + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv))