#include "rays/ruby/shader.h" #include #include "rays/ruby/image.h" #include "defs.h" RUCY_DEFINE_VALUE_OR_ARRAY_FROM_TO(Rays::Shader) #define THIS to(self) #define CHECK RUCY_CHECK_OBJECT(Rays::Shader, self) static VALUE alloc(VALUE klass) { return new_type(klass); } static const char* to_name (const Value& names, size_t index) { if (!names || !names.is_array() || index >= names.size()) return NULL; const auto& name = names[index]; if (!name) return NULL; return name.c_str(); } static Rays::ShaderEnv::NameList to_name_list (const Value& names, size_t index) { if (!names || !names.is_array() || index >= names.size()) return {}; const auto& name_or_array = names[index]; if (name_or_array.is_array()) { Rays::ShaderEnv::NameList list; for (size_t i = 0; i < name_or_array.size(); ++i) list.emplace_back(name_or_array[i].c_str()); return list; } else if (name_or_array.is_s() || name_or_array.is_sym()) return {Xot::String(name_or_array.c_str())}; else return {}; } static std::shared_ptr make_env (const Value& names, const Value& ignore_no_uniform_location_error) { bool has_names = names && names.is_array() && !names.empty(); if (!has_names && !ignore_no_uniform_location_error) return NULL; uint flags = 0; if (ignore_no_uniform_location_error) flags |= Rays::ShaderEnv::IGNORE_NO_UNIFORM_LOCATION_ERROR; return std::make_shared( to_name_list(names, 0), to_name_list(names, 1), to_name_list(names, 2), to_name( names, 3), to_name( names, 4), to_name( names, 5), to_name_list(names, 6), to_name_list(names, 7), to_name_list(names, 8), to_name_list(names, 9), to_name_list(names, 10), to_name_list(names, 11), flags); } static VALUE setup(VALUE self, VALUE fragment_shader_source, VALUE vertex_shader_source, VALUE builtin_variable_names, VALUE ignore_no_uniform_location_error) { RUCY_CHECK_OBJ(Rays::Shader, self); if (fragment_shader_source.is_nil()) argument_error(__FILE__, __LINE__); const char* fs = fragment_shader_source.c_str(); const char* vs = vertex_shader_source ? vertex_shader_source.c_str() : NULL; auto env = make_env(builtin_variable_names, ignore_no_uniform_location_error); if (env) *THIS = Rays::Shader(fs, vs, *env); else *THIS = Rays::Shader(fs, vs); } static VALUE set_uniform(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Painter#set_uniform", argc, 2, 3, 4, 5); const char* name = argv[0].c_str(); if (argv[1].is_array()) { argc = argv[1].size(); argv = argv[1].as_array(); } else { argc -= 1; argv += 1; } #define Ai(n) (argv[n].as_i()) #define Af(n) ((float) argv[n].as_f()) if (argv[0].is_i()) { switch (argc) { case 1: THIS->set_uniform(name, Ai(0)); break; case 2: THIS->set_uniform(name, Ai(0), Ai(1)); break; case 3: THIS->set_uniform(name, Ai(0), Ai(1), Ai(2)); break; case 4: THIS->set_uniform(name, Ai(0), Ai(1), Ai(2), Ai(3)); break; } } else if (argv[0].is_f()) { switch (argc) { case 1: THIS->set_uniform(name, Af(0)); break; case 2: THIS->set_uniform(name, Af(0), Af(1)); break; case 3: THIS->set_uniform(name, Af(0), Af(1), Af(2)); break; case 4: THIS->set_uniform(name, Af(0), Af(1), Af(2), Af(3)); break; } } else if (argv[0].is_kind_of(Rays::image_class())) THIS->set_uniform(name, to(argv[0])); else argument_error(__FILE__, __LINE__); #undef Ai #undef Af return self; } static VALUE get_vertex_shader_source(VALUE self) { CHECK; const char* source = THIS->vertex_shader_source(); return source ? value(source) : nil(); } static VALUE get_fragment_shader_source(VALUE self) { CHECK; const char* source = THIS->fragment_shader_source(); return source ? value(source) : nil(); } static Class cShader; void Init_rays_shader () { Module mRays = rb_define_module("Rays"); cShader = rb_define_class_under(mRays, "Shader", rb_cObject); rb_define_alloc_func(cShader, alloc); rb_define_private_method(cShader, "setup", RUBY_METHOD_FUNC(setup), 4); rb_define_private_method(cShader, "set_uniform", RUBY_METHOD_FUNC(set_uniform), -1); rb_define_method(cShader, "vertex_shader_source", RUBY_METHOD_FUNC(get_vertex_shader_source), 0); rb_define_method(cShader, "fragment_shader_source", RUBY_METHOD_FUNC(get_fragment_shader_source), 0); } namespace Rucy { template <> Rays::Shader value_to (int argc, const Value* argv, bool convert) { if (argc == 1 && argv->is_array()) { argc = argv->size(); argv = argv->as_array(); } assert(argc > 0 && argv); if (convert) { if (argv->is_s()) return Rays::Shader(argv[0].c_str()); } if (argc != 1) argument_error(__FILE__, __LINE__); return value_to(*argv, convert); } }// Rucy namespace Rays { Class shader_class () { return cShader; } }// Rays