static GPUTexture *GPU_texture_create_nD( int w, int h, int n, const float *fpixels, int depth, GPUHDRType hdr_type, int components, int samples, char err_out[256]) { GLenum type, format, internalformat; void *pixels = NULL; if (samples) { CLAMP_MAX(samples, GPU_max_color_texture_samples()); } GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->w = w; tex->h = h; tex->number = -1; tex->refcount = 1; tex->target = (n == 1) ? GL_TEXTURE_1D : (samples ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D); tex->target_base = (n == 1) ? GL_TEXTURE_1D : GL_TEXTURE_2D; tex->depth = depth; tex->fb_attachment = -1; glGenTextures(1, &tex->bindcode); if (!tex->bindcode) { if (err_out) { BLI_snprintf(err_out, 256, "GPUTexture: texture create failed: %d", (int)glGetError()); } else { fprintf(stderr, "GPUTexture: texture create failed: %d\n", (int)glGetError()); } GPU_texture_free(tex); return NULL; } if (!GPU_full_non_power_of_two_support()) { tex->w = power_of_2_max_i(tex->w); tex->h = power_of_2_max_i(tex->h); } tex->number = 0; glBindTexture(tex->target, tex->bindcode); if (depth) { type = GL_UNSIGNED_BYTE; format = GL_DEPTH_COMPONENT; internalformat = GL_DEPTH_COMPONENT; } else { type = GL_FLOAT; if (components == 4) { format = GL_RGBA; switch (hdr_type) { case GPU_HDR_NONE: internalformat = GL_RGBA8; break; /* the following formats rely on ARB_texture_float or OpenGL 3.0 */ case GPU_HDR_HALF_FLOAT: internalformat = GL_RGBA16F_ARB; break; case GPU_HDR_FULL_FLOAT: internalformat = GL_RGBA32F_ARB; break; default: break; } } else if (components == 2) { /* these formats rely on ARB_texture_rg or OpenGL 3.0 */ format = GL_RG; switch (hdr_type) { case GPU_HDR_NONE: internalformat = GL_RG8; break; case GPU_HDR_HALF_FLOAT: internalformat = GL_RG16F; break; case GPU_HDR_FULL_FLOAT: internalformat = GL_RG32F; break; default: break; } } if (fpixels && hdr_type == GPU_HDR_NONE) { type = GL_UNSIGNED_BYTE; pixels = GPU_texture_convert_pixels(w * h, fpixels); } } if (tex->target == GL_TEXTURE_1D) { glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL); if (fpixels) { glTexSubImage1D(tex->target, 0, 0, w, format, type, pixels ? pixels : fpixels); if (tex->w > w) { GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, 1); } } } else { if (samples) { glTexImage2DMultisample(tex->target, samples, internalformat, tex->w, tex->h, true); } else { glTexImage2D(tex->target, 0, internalformat, tex->w, tex->h, 0, format, type, NULL); } if (fpixels) { glTexSubImage2D(tex->target, 0, 0, 0, w, h, format, type, pixels ? pixels : fpixels); if (tex->w > w) GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w - w, tex->h); if (tex->h > h) GPU_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h - h); } } if (pixels) MEM_freeN(pixels); if (depth) { glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(tex->target_base, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); glTexParameteri(tex->target_base, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY); } else { glTexParameteri(tex->target_base, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(tex->target_base, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (tex->target_base != GL_TEXTURE_1D) { glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else glTexParameteri(tex->target_base, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); return tex; }
static GPUTexture *GPU_texture_create_nD(int w, int h, int n, float *fpixels, int depth, char err_out[256]) { GPUTexture *tex; GLenum type, format, internalformat; void *pixels = NULL; if (depth && !GLEW_ARB_depth_texture) return NULL; tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->w = w; tex->h = h; tex->number = -1; tex->refcount = 1; tex->target = (n == 1)? GL_TEXTURE_1D: GL_TEXTURE_2D; tex->depth = depth; glGenTextures(1, &tex->bindcode); if (!tex->bindcode) { if (err_out) { BLI_snprintf(err_out, 256, "GPUTexture: texture create failed: %d", (int)glGetError()); } else { fprintf(stderr, "GPUTexture: texture create failed: %d\n", (int)glGetError()); } GPU_texture_free(tex); return NULL; } if (!GPU_non_power_of_two_support()) { tex->w = power_of_2_max_i(tex->w); tex->h = power_of_2_max_i(tex->h); } tex->number = 0; glBindTexture(tex->target, tex->bindcode); if (depth) { type = GL_UNSIGNED_BYTE; format = GL_DEPTH_COMPONENT; internalformat = GL_DEPTH_COMPONENT; } else { type = GL_UNSIGNED_BYTE; format = GL_RGBA; internalformat = GL_RGBA8; if (fpixels) pixels = GPU_texture_convert_pixels(w*h, fpixels); } if (tex->target == GL_TEXTURE_1D) { glTexImage1D(tex->target, 0, internalformat, tex->w, 0, format, type, NULL); if (fpixels) { glTexSubImage1D(tex->target, 0, 0, w, format, type, pixels ? pixels : fpixels); if (tex->w > w) GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w-w, 1); } } else { glTexImage2D(tex->target, 0, internalformat, tex->w, tex->h, 0, format, type, NULL); if (fpixels) { glTexSubImage2D(tex->target, 0, 0, 0, w, h, format, type, pixels ? pixels : fpixels); if (tex->w > w) GPU_glTexSubImageEmpty(tex->target, format, w, 0, tex->w-w, tex->h); if (tex->h > h) GPU_glTexSubImageEmpty(tex->target, format, 0, h, w, tex->h-h); } } if (pixels) MEM_freeN(pixels); if (depth) { glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(tex->target, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(tex->target, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); glTexParameteri(tex->target, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY); } else { glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (tex->target != GL_TEXTURE_1D) { /* CLAMP_TO_BORDER is an OpenGL 1.3 core feature */ GLenum wrapmode = (depth || tex->h == 1)? GL_CLAMP_TO_EDGE: GL_CLAMP_TO_BORDER; glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, wrapmode); glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, wrapmode); #if 0 float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); #endif } else glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); return tex; }
GPUTexture *GPU_texture_create_3D(int w, int h, int depth, int channels, const float *fpixels) { GLenum type, format, internalformat; void *pixels = NULL; GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); tex->w = w; tex->h = h; tex->depth = depth; tex->number = -1; tex->refcount = 1; tex->target = GL_TEXTURE_3D; tex->target_base = GL_TEXTURE_3D; glGenTextures(1, &tex->bindcode); if (!tex->bindcode) { fprintf(stderr, "GPUTexture: texture create failed: %d\n", (int)glGetError()); GPU_texture_free(tex); return NULL; } tex->number = 0; glBindTexture(tex->target, tex->bindcode); GPU_ASSERT_NO_GL_ERRORS("3D glBindTexture"); type = GL_FLOAT; if (channels == 4) { format = GL_RGBA; internalformat = GL_RGBA8; } else { format = GL_RED; internalformat = GL_INTENSITY8; } /* 3D textures are quite heavy, test if it's possible to create them first */ glTexImage3D(GL_PROXY_TEXTURE_3D, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL); bool rescale = false; int r_width; glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width); while (r_width == 0) { rescale = true; tex->w /= 2; tex->h /= 2; tex->depth /= 2; glTexImage3D(GL_PROXY_TEXTURE_3D, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, NULL); glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &r_width); } /* really unlikely to happen but keep this just in case */ tex->w = max_ii(tex->w, 1); tex->h = max_ii(tex->h, 1); tex->depth = max_ii(tex->depth, 1); #if 0 if (fpixels) pixels = GPU_texture_convert_pixels(w*h*depth, fpixels); #endif GPU_ASSERT_NO_GL_ERRORS("3D glTexImage3D"); /* hardcore stuff, 3D texture rescaling - warning, this is gonna hurt your performance a lot, but we need it * for gooseberry */ if (rescale && fpixels) { /* FIXME: should these be floating point? */ const unsigned int xf = w / tex->w, yf = h / tex->h, zf = depth / tex->depth; float *tex3d = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->depth, "tex3d"); GPU_print_error_debug("You need to scale a 3D texture, feel the pain!"); for (unsigned k = 0; k < tex->depth; k++) { for (unsigned j = 0; j < tex->h; j++) { for (unsigned i = 0; i < tex->w; i++) { /* obviously doing nearest filtering here, * it's going to be slow in any case, let's not make it worse */ float xb = i * xf; float yb = j * yf; float zb = k * zf; unsigned int offset = k * (tex->w * tex->h) + i * tex->h + j; unsigned int offset_orig = (zb) * (w * h) + (xb) * h + (yb); if (channels == 4) { tex3d[offset * 4] = fpixels[offset_orig * 4]; tex3d[offset * 4 + 1] = fpixels[offset_orig * 4 + 1]; tex3d[offset * 4 + 2] = fpixels[offset_orig * 4 + 2]; tex3d[offset * 4 + 3] = fpixels[offset_orig * 4 + 3]; } else tex3d[offset] = fpixels[offset_orig]; } } } glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, tex3d); MEM_freeN(tex3d); } else { if (fpixels) { glTexImage3D(tex->target, 0, internalformat, tex->w, tex->h, tex->depth, 0, format, type, fpixels); GPU_ASSERT_NO_GL_ERRORS("3D glTexSubImage3D"); } } glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); if (pixels) MEM_freeN(pixels); GPU_texture_unbind(tex); return tex; }