ext/gl_buffer.c in ray-0.1.1 vs ext/gl_buffer.c in ray-0.2.0

- old
+ new

@@ -46,25 +46,71 @@ *ptr = say_buffer_create(ray_get_vtype(vtype), ray_buf_type(type), 256); return self; } -/* Unbinds any buffer that was bound. */ +/* @return [Boolean] True if the buffer has instance-specific data */ static +VALUE ray_gl_buffer_has_instance(VALUE self) { + return say_buffer_has_instance(ray_rb2buffer(self)) ? Qtrue : Qfalse; +} + +/* Unbinds any bound buffer */ +static VALUE ray_gl_buffer_unbind(VALUE self) { say_buffer_unbind(); return Qnil; } -/* Binds the receiver */ +/* Unbinds any bound VBO from array buffer */ static +VALUE ray_gl_buffer_unbind_vbo(VALUE self) { + say_buffer_unbind_vbo(); + return Qnil; +} + +/* Binds the receiver, allowing to draw data stored in it */ +static VALUE ray_gl_buffer_bind(VALUE self) { say_buffer_bind(ray_rb2buffer(self)); return self; } +/* Binds the receivers's VBO as an OpenGL array buffer */ +static +VALUE ray_gl_buffer_bind_vbo(VALUE self) { + say_buffer_bind_vbo(ray_rb2buffer(self)); + return self; +} + /* + * Binds the receivers's VBO containing instance data as an OpenGL array buffer + */ +static +VALUE ray_gl_buffer_bind_instance_vbo(VALUE self) { + say_buffer_bind_instance_vbo(ray_rb2buffer(self)); + return self; +} + +/* + * @return [Integer] The identifier of the OpenGL buffer used by the buffer + */ +static +VALUE ray_gl_buffer_vbo(VALUE self) { + return ULONG2NUM(say_buffer_get_vbo(ray_rb2buffer(self))); +} + +/* + * @return [Integer] The identifier of the OpenGL buffer used by the buffer to + * store per-instance data + */ +static +VALUE ray_gl_buffer_instance_vbo(VALUE self) { + return ULONG2NUM(say_buffer_get_instance_vbo(ray_rb2buffer(self))); +} + +/* * @overload [](id) * @param [Integer] id * @return [Ray::GL::Vertex, Ray::Vertex] The vertex at the given index. */ static @@ -121,12 +167,83 @@ memcpy(say_buffer_get_vertex(buf, index), ptr, byte_size); return vertex; } +/* + * @overload get_instance(id) + * @param [Integer] id + * @return [Ray::GL::Vertex::Instance] The instance at the given index + */ +static +VALUE ray_gl_buffer_get_instance(VALUE self, VALUE i) { + say_buffer *buf = ray_rb2buffer(self); + if (!say_buffer_has_instance(buf)) + rb_raise(rb_eRuntimeError, "buffer has no instance data"); + + size_t size = say_buffer_get_instance_size(buf); + size_t index = NUM2ULONG(i); + + if (index >= size) + return Qnil; + + VALUE klass = rb_const_get(rb_iv_get(self, "@vertex_type"), + rb_intern("Instance")); + VALUE object = rb_funcall(klass, RAY_METH("allocate"), 0); + + size_t byte_size = NUM2INT(rb_iv_get(klass, "@vertex_instance_size")); + + void *ptr = NULL; + Data_Get_Struct(object, void, ptr); + + memcpy(ptr, say_buffer_get_instance(buf, index), byte_size); + + return object; +} + /* + * @overload set_intance(id, value) + * @param [Integer] id + * @param [Ray::GL::Vertex::Instance] value The instance to set the given + * index. + */ +static +VALUE ray_gl_buffer_set_instance(VALUE self, VALUE i, VALUE vertex) { + rb_check_frozen(self); + + say_buffer *buf = ray_rb2buffer(self); + + if (!say_buffer_has_instance(buf)) + rb_raise(rb_eRuntimeError, "buffer has no instance data"); + + VALUE klass = rb_const_get(rb_iv_get(self, "@vertex_type"), + rb_intern("Instance")); + if (!RAY_IS_A(vertex, klass)) { + rb_raise(rb_eTypeError, "Can't convert %s into %s", + RAY_OBJ_CLASSNAME(vertex), rb_class2name(klass)); + } + + size_t size = say_buffer_get_instance_size(buf); + size_t index = NUM2ULONG(i); + + if (index >= size) { + rb_raise(rb_eRangeError, "%zu is outside of range 0...%zu", + size, index); + } + + size_t byte_size = NUM2INT(rb_iv_get(klass, "@vertex_instance_size")); + + void *ptr = NULL; + Data_Get_Struct(vertex, void, ptr); + + memcpy(say_buffer_get_instance(buf, index), ptr, byte_size); + + return vertex; +} + +/* * @overload update(range = 0...size) * @param [Range<Integer>] range Indices of vertices to update * * Updates a part of the buffer. * @@ -174,10 +291,64 @@ } return self; } +/* + * @overload update_instance(range = 0...size) + * @param [Range<Integer>] range (see #update) + * + * Updates a part of the buffer's instance-specific data + * + * @overload update_instance(first, size) + * @param (see #update) + */ +static +VALUE ray_gl_buffer_update_instance(int argc, VALUE *argv, VALUE self) { + say_buffer *buf = ray_rb2buffer(self); + size_t max_index = say_buffer_get_instance_size(buf); + + if (!say_buffer_has_instance(buf)) + rb_raise(rb_eRuntimeError, "buffer has no per-instance data"); + + if (argc == 0) + say_buffer_update_instance(buf); + else if (argc == 2) { + size_t begin = NUM2ULONG(argv[0]); + size_t end = NUM2ULONG(argv[1]); + + if (end > max_index) + end = max_index; + + if (begin > end || begin > max_index) + return self; + + size_t size = (end - begin) + 1; + + say_buffer_update_instance_part(buf, begin, size); + } + else { + VALUE range; + rb_scan_args(argc, argv, "1", &range); /* raise exception */ + + size_t begin = NUM2ULONG(rb_funcall(range, RAY_METH("begin"), 0)); + size_t end = NUM2ULONG(rb_funcall(range, RAY_METH("end"), 0)); + + if (end > max_index) + end = max_index; + + if (begin > end || begin > max_index) + return self; + + size_t size = (end - begin) + 1; + + say_buffer_update_instance_part(buf, begin, size); + } + + return self; +} + /* @return [Integer] Size of the buffer (amount of vertices it contains) */ static VALUE ray_gl_buffer_size(VALUE self) { return ULONG2NUM(say_buffer_get_size(ray_rb2buffer(self))); } @@ -193,11 +364,37 @@ rb_check_frozen(self); say_buffer_resize(ray_rb2buffer(self), NUM2ULONG(size)); return self; } +/* @return [Integer] Amount of per-instance blocks in the buffer */ +static +VALUE ray_gl_buffer_instance_size(VALUE self) { + say_buffer *buf = ray_rb2buffer(self); + if (!say_buffer_has_instance(buf)) + return Qnil; + return ULONG2NUM(say_buffer_get_instance_size(buf)); +} + /* + * @overload resize_instance(size) + * @param [Integere] size (see #resize) + * + * Resizes the buffer's instance data, alsos causing it to be updated. + */ +static +VALUE ray_gl_buffer_resize_instance(VALUE self, VALUE size) { + rb_check_frozen(self); + say_buffer *buf = ray_rb2buffer(self); + if (!say_buffer_has_instance(buf)) + rb_raise(rb_eRuntimeError, "buffer has no per-instance data"); + say_buffer_resize_instance(buf, NUM2ULONG(size)); + return self; +} + + +/* * Document-class: Ray::GL::Buffer * * Buffers are a low-level way to push vertices onto the GPU so you can draw * them. They are used interally by higher level classes, such as * Ray::BufferRenderer; Ray also keeps global buffers to store the vertices of @@ -209,16 +406,40 @@ void Init_ray_gl_buffer() { ray_cGLBuffer = rb_define_class_under(ray_mGL, "Buffer", rb_cObject); rb_define_alloc_func(ray_cGLBuffer, ray_gl_buffer_alloc); rb_define_method(ray_cGLBuffer, "initialize", ray_gl_buffer_init, 2); + rb_define_method(ray_cGLBuffer, "has_instance?", ray_gl_buffer_has_instance, + 0); + rb_define_singleton_method(ray_cGLBuffer, "unbind", ray_gl_buffer_unbind, 0); + rb_define_singleton_method(ray_cGLBuffer, "unbind_vbo", + ray_gl_buffer_unbind_vbo, 0); rb_define_method(ray_cGLBuffer, "bind", ray_gl_buffer_bind, 0); + rb_define_method(ray_cGLBuffer, "bind_vbo", ray_gl_buffer_bind_vbo, 0); + rb_define_method(ray_cGLBuffer, "bind_instance_vbo", + ray_gl_buffer_bind_instance_vbo, 0); + rb_define_method(ray_cGLBuffer, "vbo", ray_gl_buffer_vbo, 0); + rb_define_method(ray_cGLBuffer, "instance_vbo", ray_gl_buffer_instance_vbo, + 0); + rb_define_method(ray_cGLBuffer, "[]", ray_gl_buffer_get, 1); rb_define_method(ray_cGLBuffer, "[]=", ray_gl_buffer_set, 2); + rb_define_method(ray_cGLBuffer, "get_instance", ray_gl_buffer_get_instance, + 1); + rb_define_method(ray_cGLBuffer, "set_instance", ray_gl_buffer_set_instance, + 2); + rb_define_method(ray_cGLBuffer, "update", ray_gl_buffer_update, -1); + rb_define_method(ray_cGLBuffer, "update_instance", + ray_gl_buffer_update_instance, -1); rb_define_method(ray_cGLBuffer, "size", ray_gl_buffer_size, 0); rb_define_method(ray_cGLBuffer, "resize", ray_gl_buffer_resize, 1); + + rb_define_method(ray_cGLBuffer, "instance_size", ray_gl_buffer_instance_size, + 0); + rb_define_method(ray_cGLBuffer, "resize_instance", + ray_gl_buffer_resize_instance, 1); }