Importing Blender object with Image texture in OpenGL - c++

I tried to import obects with image texture from blender to opengl (using Devil) but thats what i got.
It seems like the images overlap all the screen covering all the objects in the scene.
How can i solve this problem?
I think that the problem might be my blender model but i tried everything i could.
I am using this function for the texture
int LoadGLTextures(const aiScene* scene)
{
ILboolean success;
/* Before calling ilInit() version should be checked. */
if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION)
{
ILint test = ilGetInteger(IL_VERSION_NUM);
/// wrong DevIL version ///
std::string err_msg = "Wrong DevIL version. Old devil.dll in system32/SysWow64?";
char* cErr_msg = (char*)err_msg.c_str();
return -1;
}
ilInit(); /* Initialization of DevIL */
//if (scene->HasTextures()) abortGLInit("Support for meshes with embedded textures is not implemented");
/* getTexture Filenames and Numb of Textures */
for (unsigned int m = 0; m < scene->mNumMaterials; m++)
{
int texIndex = 0;
aiReturn texFound = AI_SUCCESS;
aiString path; // filename
while (texFound == AI_SUCCESS)
{
texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
textureIdMap[path.data] = NULL; //fill map with textures, pointers still NULL yet
texIndex++;
}
}
int numTextures = textureIdMap.size();
/* array with DevIL image IDs */
ILuint* imageIds = NULL;
imageIds = new ILuint[numTextures];
/* generate DevIL Image IDs */
ilGenImages(numTextures, imageIds); /* Generation of numTextures image names */
/* create and fill array with GL texture ids */
textureIds = new GLuint[numTextures];
glGenTextures(numTextures, textureIds); /* Texture name generation */
/* define texture path */
//std::string texturepath = "../../../test/models/Obj/";
/* get iterator */
std::map<std::string, GLuint*>::iterator itr = textureIdMap.begin();
for (int i = 0; i < numTextures; i++)
{
//save IL image ID
std::string filename = (*itr).first; // get filename
(*itr).second = &textureIds[i]; // save texture id for filename in map
itr++; // next texture
ilBindImage(imageIds[i]); /* Binding of DevIL image name */
std::string fileloc = basepath + filename; /* Loading of image */
success = ilLoadImage((const wchar_t*)fileloc.c_str());
fprintf(stdout, "Loading Image: %s\n", fileloc.data());
if (success) /* If no error occured: */
{
success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE); /* Convert every colour component into
unsigned byte. If your image contains alpha channel you can replace IL_RGB with IL_RGBA */
if (!success)
{
/* Error occured */
fprintf(stderr, "Couldn't convert image");
return -1;
}
//glGenTextures(numTextures, &textureIds[i]); /* Texture name generation */
glBindTexture(GL_TEXTURE_2D, textureIds[i]); /* Binding of texture name */
//redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear
interpolation for magnification filter */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear
interpolation for minifying filter */
glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH),
ilGetInteger(IL_IMAGE_HEIGHT), 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE,
ilGetData()); /* Texture specification */
}
else
{
/* Error occured */
fprintf(stderr, "Couldn't load Image: %s\n", fileloc.data());
}
}
ilDeleteImages(numTextures, imageIds); /* Because we have already copied image data into texture data
we can release memory used by image. */
//Cleanup
delete[] imageIds;
imageIds = NULL;
//return success;
return TRUE;
}

Related

How do I handle gamma correction and colors in a PNG file?

I'm working on an image drawing program and I'm getting really confused about colorspaces. The more I read about gamma, the less I know (this wasn't much help).
Internally, the drawing program will store images as 8-bit sRGB and then convert to 16-bit linear for compositing and filtering operations. Perhaps I should store 16-bit linear and then convert to 8-bit sRGB when exporting? I'd love some advice on this. Currently, I'm not really sure what colorspace the RGB pixels are being interpreted as by Qt!
As I understand it, sRGB is close to the colorspace that most monitors use so converting from sRGB to the monitor colorspace doesn't change the image data much. Looking at the sRGB data as if it were in the right colorspace will probably be pretty close.
Goals
The drawing program displays sRGB images. I want to save these sRGB images to a PNG file. When I open this PNG file (with Preview on a Mac) on the same computer that the image was created on, it should look exactly the same as what the artist sees in the drawing program and have the same color values (checked with Digital Color Meter). Different monitors and different operating systems use different colorspaces. These colorspaces might not "fit" the colorspace of the system used to create the image. When I open the PNG on a different monitor or even a different computer, the image should look as similar as possible to the original but probably have different color values.
Experiments
The drawing program seems to be displaying the images correctly (I think). The problem is the PNG. I'm using Qt and saving the image with QImage::save. I'm willing to use libPNG if I need more control.
For testing, I'm drawing a 5x5 image with the color values 0 63 127 191 255 for red and green.
Drawing program screenshots
When I sample the image rendered by the drawing program with Digital Color Meter, the pixel values are unchanged. The pixel at 3,3 sampled with DCM is 191 191 0 as it should be. There is a clear contrast between each of the pixels.
When I take a screenshot, the pixel values in the screenshot file are different. The pixel at 3,3 sampled with DCM when viewing in Preview is 192 191 0. The pixel at 3,3 stored in the file is 140 126 4. I should note that the screenshot file has an sRGB chunk with a rendering intent perceptual.
When I crop the screenshot to a 5x5 image using Preview, the sRGB chunk is replaced with the gAMA and cHRM chunks that correspond to sRGB (I used pngcheck).
gAMA
0.45455
cHRM
White x = 0.3127 y = 0.329, Red x = 0.64 y = 0.33
Green x = 0.3 y = 0.6, Blue x = 0.15 y = 0.06
Both versions have the same pixel values stored in the file. They also have the same pixel values when sampled with DCM when viewing in Preview.
Below is the cropped screenshot (it's really tiny!).
The best way of saving a drawing seems to be taking a screenshot but even that's not perfect.
Test programs
Qt program
#include <QtGui/qimage.h>
int main() {
QImage image{5, 5, QImage::Format_RGB32};
const int values[5] = {0, 63, 127, 191, 255};
for (int r = 0; r != 5; ++r) {
for (int g = 0; g != 5; ++g) {
image.setPixel(g, r, qRgb(values[r], values[g], 0));
}
}
image.save("qt.png");
}
This program produces the same output as the libpng program except that Qt adds a pHYs chunk. The output looks similar to the desired output but there is less contrast between the pixels and the pixel values are significantly off.
Libpng program
#include <cmath>
#include <iostream>
#include <libpng16/png.h>
png_byte srgb_lut[256];
void initLut(const double exponent) {
for (int i = 0; i != 256; ++i) {
srgb_lut[i] = std::round(std::pow(i / 255.0, exponent) * 255.0);
}
}
int main() {
std::FILE *file = std::fopen("libpng.png", "wb");
if (!file) {
std::cerr << "Failed to open file\n";
return 1;
}
png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!pngPtr) {
std::fclose(file);
std::cout << "Failed to initialize png write struct\n";
return 1;
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_write_struct(&pngPtr, nullptr);
std::fclose(file);
std::cout << "Failed to initialize png info struct\n";
return 1;
}
if (setjmp(png_jmpbuf(pngPtr))) {
png_destroy_write_struct(&pngPtr, &infoPtr);
std::fclose(file);
std::cout << "Failed to set jmp buf\n";
return 1;
}
png_init_io(pngPtr, file);
png_set_IHDR(
pngPtr,
infoPtr,
5,
5,
8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
//png_set_gAMA_fixed(pngPtr, infoPtr, 100000);
//png_set_sRGB_gAMA_and_cHRM(pngPtr, infoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
//png_set_sRGB(pngPtr, infoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
//initLut(2.2);
//initLut(1.0/2.2);
initLut(1.0);
png_bytep rows[5];
png_color imageData[5][5];
const png_byte values[5] = {0, 63, 127, 191, 255};
for (int r = 0; r != 5; ++r) {
for (int g = 0; g != 5; ++g) {
imageData[r][g] = {srgb_lut[values[r]], srgb_lut[values[g]], 0};
}
rows[r] = reinterpret_cast<png_bytep>(&imageData[r][0]);
}
png_set_rows(pngPtr, infoPtr, rows);
png_write_png(pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, nullptr);
png_destroy_write_struct(&pngPtr, &infoPtr);
std::fclose(file);
}
As I said the previous section, the output is similar to the desired output but there is reduced contrast. The pixel at 3,3 sampled with DCM when viewing in Preview is 186 198 0 which is way off.
I'm really glad that Qt is producing the same output as libpng even though the output isn't what I want it to be. It means that I could switch to libpng if I needed to.
Sample program
This program samples a pixel from a PNG. I'm pretty sure it doesn't do any color space conversion and just gives me the value stored in the file.
#include <iostream>
#include <libpng16/png.h>
int main(int argc, char **argv) {
if (argc != 4) {
std::cout << "sample <file> <x> <y>\n";
return 1;
}
const int x = std::atoi(argv[2]);
const int y = std::atoi(argv[3]);
if (x < 0 || y < 0) {
std::cerr << "Coordinates out of range\n";
return 1;
}
std::FILE *file = std::fopen(argv[1], "rb");
if (!file) {
std::cerr << "Failed to open file\n";
return 1;
}
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!pngPtr) {
std::fclose(file);
std::cerr << "Failed to initialize read struct\n";
return 1;
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_read_struct(&pngPtr, nullptr, nullptr);
std::fclose(file);
std::cerr << "Failed to initialize info struct\n";
return 1;
}
if (setjmp(png_jmpbuf(pngPtr))) {
// Pssh, who needs exceptions anyway?
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
std::fclose(file);
std::cerr << "Failed to set jmp buf\n";
return 1;
}
png_init_io(pngPtr, file);
// Does this prevent libpng from changing the color values?
png_set_gamma(pngPtr, PNG_GAMMA_LINEAR, PNG_GAMMA_LINEAR);
png_read_png(pngPtr, infoPtr, PNG_TRANSFORM_STRIP_ALPHA, nullptr);
png_bytepp rows = png_get_rows(pngPtr, infoPtr);
const int width = png_get_image_width(pngPtr, infoPtr);
const int height = png_get_image_height(pngPtr, infoPtr);
if (x >= width || y >= height) {
// Pssh, who needs RAII anyway?
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
std::fclose(file);
std::cerr << "Coordinates out of range\n";
return 1;
}
png_bytep row = rows[y];
for (int c = 0; c != 3; ++c) {
std::cout << static_cast<int>(row[x * 3 + c]) << ' ';
}
std::cout << '\n';
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
std::fclose(file);
}
What I'm really trying to do
I need to modify the libpng test program so that the pixel values are 0 63 127 191 255 when opened with Preview and sampled with Digital Color Meter. Sounds like a simple task but it most definitely isn't. There's some commented code in the libpng test program of things I've tried. None of them produces the desired output. What's really frustrating is that Chrome and Preview produce different results. I have no idea which is correct or closest to correct or what "correct" even means.
The more I read about this, the more I think I should just settle for "oh well, it's noticeably wrong but I guess it's good enough *sigh*".
Viewing Experiments
I wrote two identical programs for viewing PNGs. They both produce the desired output (sampling with DCM returns 0 63 127 191 255).
Qt Viewer
#include <iostream>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qmainwindow.h>
#include <QtWidgets/qapplication.h>
int main(int argc, char **argv) {
if (argc != 2) {
std::cerr << "qt_render <file>\n";
return EXIT_FAILURE;
}
QImage image{argv[1]};
if (image.isNull()) {
std::cerr << "Failed to load image\n";
return EXIT_FAILURE;
}
image = image.scaled(image.size() * 64);
QApplication app{argc, argv};
QMainWindow window;
window.setWindowTitle(argv[1]);
window.setFixedSize(image.size());
QLabel label{&window};
QPixmap pixmap;
if (!pixmap.convertFromImage(image)) {
std::cerr << "Failed to convert surface to texture\n";
return EXIT_FAILURE;
}
label.setPixmap(pixmap);
label.setFixedSize(image.size());
window.show();
return app.exec();
}
SDL2 Libpng Viewer
#include <iostream>
#include <SDL2/SDL.h>
#include <libpng16/png.h>
template <typename... Args>
[[noreturn]] void fatalError(Args &&... args) {
(std::cerr << ... << args) << '\n';
throw std::exception{};
}
void checkErr(const int errorCode) {
if (errorCode != 0) {
fatalError("Error: ", SDL_GetError());
}
}
template <typename T>
T *checkNull(T *ptr) {
if (ptr == nullptr) {
fatalError("Error: ", SDL_GetError());
} else {
return ptr;
}
}
struct FileCloser {
void operator()(std::FILE *file) const noexcept {
std::fclose(file);
}
};
using File = std::unique_ptr<std::FILE, FileCloser>;
File openFile(const char *path, const char *mode) {
std::FILE *file = std::fopen(path, mode);
if (!file) {
fatalError("Failed to open file");
} else {
return File{file};
}
}
struct WindowDestroyer {
void operator()(SDL_Window *window) const noexcept {
SDL_DestroyWindow(window);
}
};
using Window = std::unique_ptr<SDL_Window, WindowDestroyer>;
struct SurfaceDestroyer {
void operator()(SDL_Surface *surface) const noexcept {
SDL_FreeSurface(surface);
}
};
using Surface = std::unique_ptr<SDL_Surface, SurfaceDestroyer>;
struct TextureDestroyer {
void operator()(SDL_Texture *texture) const noexcept {
SDL_DestroyTexture(texture);
}
};
using Texture = std::unique_ptr<SDL_Texture, TextureDestroyer>;
struct RendererDestroyer {
void operator()(SDL_Renderer *renderer) const noexcept {
SDL_DestroyRenderer(renderer);
}
};
using Renderer = std::unique_ptr<SDL_Renderer, RendererDestroyer>;
class SurfaceLock {
public:
explicit SurfaceLock(SDL_Surface *surface)
: surface{surface} {
SDL_LockSurface(surface);
}
~SurfaceLock() {
SDL_UnlockSurface(surface);
}
private:
SDL_Surface *surface;
};
Surface createSurface(png_structp pngPtr, png_infop infoPtr) {
const png_bytepp rows = png_get_rows(pngPtr, infoPtr);
const int width = png_get_image_width(pngPtr, infoPtr);
const int height = png_get_image_height(pngPtr, infoPtr);
Surface surface = Surface{checkNull(SDL_CreateRGBSurfaceWithFormat(
0, width, height, 24, SDL_PIXELFORMAT_RGB24
))};
{
SurfaceLock lock{surface.get()};
for (int y = 0; y != height; ++y) {
uint8_t *dst = static_cast<uint8_t *>(surface->pixels);
dst += y * surface->pitch;
std::memcpy(dst, rows[y], width * 3);
}
}
return surface;
}
void doMain(int argc, char **argv) {
if (argc != 2) {
fatalError("sdl_render <file>");
}
File file = openFile(argv[1], "rb");
png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!pngPtr) {
fatalError("Failed to initialize read struct\n");
}
png_infop infoPtr = png_create_info_struct(pngPtr);
if (!infoPtr) {
png_destroy_read_struct(&pngPtr, nullptr, nullptr);
fatalError("Failed to initialize info struct\n");
}
if (setjmp(png_jmpbuf(pngPtr))) {
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
fatalError("Failed to set jmp buf");
}
png_init_io(pngPtr, file.get());
png_read_png(pngPtr, infoPtr, PNG_TRANSFORM_STRIP_ALPHA, nullptr);
Surface surface = createSurface(pngPtr, infoPtr);
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
checkErr(SDL_Init(SDL_INIT_VIDEO));
std::atexit(SDL_Quit);
Window window = Window{checkNull(SDL_CreateWindow(
argv[1], SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, surface->w * 64, surface->h * 64, 0
))};
Renderer renderer = Renderer{checkNull(SDL_CreateRenderer(
window.get(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
))};
Texture texture = Texture{checkNull(SDL_CreateTextureFromSurface(
renderer.get(), surface.get()
))};
surface.reset();
while (true) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
return;
}
}
SDL_RenderCopy(renderer.get(), texture.get(), nullptr, nullptr);
SDL_RenderPresent(renderer.get());
}
}
int main(int argc, char **argv) {
try {
doMain(argc, argv);
} catch (...) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I'm tempted to write an SDL2, OpenGL, Libpng viewer just to be sure but OpenGL is kind of a hassle. My application is aimed at making sprites and textures for games so if it works with the SDL2 render API and OpenGL then I guess everything is fine. I haven't done any experiments with external monitors yet. Putting sRGB, gAMA and cHRM chunks in the PNG doesn't have any effect on the output of either viewer. I'm not sure if this is good or bad. On the surface, it looks like my problem has just disappeared. I'd still like someone to explain my observations.
ColorSync Utility
I've discovered a new tool and now I think I know what's going on...
This can be accomplished with ImageMagick.
FYI different photo editing programs manage PNG metadata differently, so be aware of this. For example, Mac's Preview app automatically attaches a sRGB IEC61966-2.1 color profile to edited PNG files that do not have an existing color profile. This behavior not only changes the colors, but also impacts the file size.
To make image file comparisons, I use the following ImageMagick command (for each file):
magick identify -verbose insert/image/filepath/here.png
Also, if you want to strip all PNG metadata, use the following ImageMagick command:
convert -strip insert/original/image/filepath/here.png insert/NEW/image/filepath/here2.png

How to display an image into an XCB window?

I'm having trouble displaying an image (PNG extracted with libpng) into an XCB window, it is always entirely empty/white. I'm pretty sure the PNG extraction is correct since I can perfectly re-write it into another file.
I've tried everything I found (explanations, guides, documentation) and I'm running out of ideas:
Creating an xcb_pixmap_t calling xcb_create_pixmap_from_bitmap_data() with the data taken from the PNG, then calling xcb_copy_area() into the EXPOSE part of the event loop.
Creating an xcb_image_t* calling xcb_image_create_from_bitmap_data() then trying to map it to the window with xcb_image_put(). I've even tried to display each pixel with xcb_image_put_pixel(), but without success.
Code sample:
xcb_pixmap_t pixmap = xcb_create_pixmap_from_bitmap_data(
connection, // xcb_connect(0, 0) (type: xcb_connection_t*)
window, // xcb_generate_id(connection) (type: xcb_window_t)
img.getData(), // uint8_t*
img.getWidth(), // 128
img.getHeight(), // 128
img.getBitDepth(), // 8
screen->black_pixel, // screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data (type: xcb_screen_t*)
screen->white_pixel,
nullptr);
// "img" is an instance of my own custom class, result of PNG reading
xcb_image_t* image = xcb_image_create_from_bitmap_data(
img.getData(),
img.getWidth(),
img.getHeight()); // image->data seems fine
xcb_image_put(connection,
window,
graphicsContext,
image, 0, 0, 0); // This does nothing
for (unsigned int i = 0; i < screen->height_in_pixels; ++i)
for (unsigned int j = 0; j < screen->width_in_pixels; ++j)
xcb_image_put_pixel(image, j, i, 0); // Displays nothing
[...]
// Into event loop
case XCB_EXPOSE: {
xcb_expose_event_t* exposeEvent = reinterpret_cast<xcb_expose_event_t*>(event);
xcb_copy_area(connection,
pixmap,
window,
graphicsContext,
exposeEvent->x, exposeEvent->y, // Top left x & y coordinates of the source's region to copy
exposeEvent->x, exposeEvent->y, // Top left x & y coordinates of the destination's region to copy to
exposeEvent->width,
exposeEvent->height);
xcb_flush(connection);
break;
}
From the examples I found I saw that it didn't need a colormap, but could that be the case? Could anyone tell me where I've gone wrong?
I threw together a simple xcb image viewer about 4 years ago, but just noticed this question, so apologies for the necromancy.
It uses xcb_image, stb_image and nanosvg, but compiles to a relatively small static binary (with a musl or uclibc toolchain)
#include <xcb/xcb.h>
#include <xcb/xcb_image.h>
#define STBI_NO_HDR
#define STBI_NO_LINEAR
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
int main(int argc, char **argv){
xcb_connection_t *c = xcb_connect(0, 0);
xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
int w, h, n,
depth = s->root_depth,
win_class = XCB_WINDOW_CLASS_INPUT_OUTPUT,
format = XCB_IMAGE_FORMAT_Z_PIXMAP;
xcb_colormap_t colormap = s->default_colormap;
xcb_drawable_t win = xcb_generate_id(c);
xcb_gcontext_t gc = xcb_generate_id(c);
xcb_pixmap_t pixmap = xcb_generate_id(c);
xcb_generic_event_t *ev;
xcb_image_t *image;
NSVGimage *shapes = NULL;
NSVGrasterizer *rast = NULL;
char *data = NULL;
unsigned *dp;
size_t i, len;
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
value_mask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
values[] = { s->black_pixel, value_mask };
if (argc<2) return -1;
if ((data = stbi_load(argv[1], &w, &h, &n, 4)))
;
else if ((shapes = nsvgParseFromFile(argv[1], "px", 96.0f))) {
w = (int)shapes->width;
h = (int)shapes->height;
rast = nsvgCreateRasterizer();
data = malloc(w*h*4);
nsvgRasterize(rast, shapes, 0,0,1, data, w, h, w*4);
}else return -1;
for(i=0,len=w*h,dp=(unsigned *)data;i<len;i++) //rgba to bgra
dp[i]=dp[i]&0xff00ff00|((dp[i]>>16)&0xFF)|((dp[i]<<16)&0xFF0000);
xcb_create_window(c,depth,win,s->root,0,0,w,h,1,win_class,s->root_visual,mask,values);
xcb_create_pixmap(c,depth,pixmap,win,w,h);
xcb_create_gc(c,gc,pixmap,0,NULL);
image = xcb_image_create_native(c,w,h,format,depth,data,w*h*4,data);
xcb_image_put(c, pixmap, gc, image, 0, 0, 0);
xcb_image_destroy(image);
xcb_map_window(c, win);
xcb_flush(c);
while ((ev = xcb_wait_for_event(c))) {
switch (ev->response_type & ~0x80){
case XCB_EXPOSE: {
xcb_expose_event_t *x = (xcb_expose_event_t *)ev;
xcb_copy_area(c,pixmap,win,gc,x->x,x->y,x->x,x->y,x->width,x->height);
xcb_flush(c);
}break;
case XCB_BUTTON_PRESS: goto end;
default: break;
}
}
end:
xcb_free_pixmap(c, pixmap);
xcb_disconnect(c);
return 0;
}

Make transparency not show what is behind the window in opengl with c++

I am making a 2 dimensional image in opengl with C++, and am running into an interesting issue. Whenever I try to draw a partially transparent polygon on my image, it makes the window itself partially transparent where the polygon is. For example, I can see whatever is behind my window (e.g. my code) when I am running the program (which I don't want). I can also see the image behind the polygon (which I do want). Is there any way I can turn the "transparent window" behavior off? I have included what I feel to be relevant portions of the code below:
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // I have tried 1.0f for the alpha value too
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
glDisable(GL_POINT_SMOOTH);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
// other code to draw my opaque "background" object
// Draw my partially transparent quad (note: this is where the window itself becomes partially transparent)
glBegin(GL_QUADS); // Begin drawing quads
glColor4f(1.0,1.0,1.0,0.5); // Make a white quad with .5 alpha
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, .05);
glVertex2f(0.5, -0.5);
glVertex2f(-0.5, -0.5);
glEnd();
Other relevant information:
I am running CentOS 6
I am fairly new to opengl, and am working on the code after a prior developer, so I could be missing something trivial
It is using the X Windows system
Here is the X Window creation code further debug, the problem is likely here rather than the opengl code above.
/* The simplest possible Linux OpenGL program? Maybe...
Modification for creating a RGBA window (transparency with compositors)
by Wolfgang 'datenwolf' Draxinger
(c) 2002 by FTB. See me in comp.graphics.api.opengl
(c) 2011 Wolfgang Draxinger. See me in comp.graphics.api.opengl and on StackOverflow
License agreement: This source code is provided "as is". You
can use this source code however you want for your own personal
use. If you give this source code to anybody else then you must
leave this message in it.
--
<\___/>
/ O O \
\_____/ FTB.
--
datenwolf
------------------------------------------------------------------------*/
static void createTheWindow() {
XEvent event;
int x, y, attr_mask;
XSizeHints hints;
XWMHints *StartupState;
XTextProperty textprop;
XSetWindowAttributes attr;
static char *title = "Fix me";
/* Connect to the X server */
Xdisplay = XOpenDisplay(NULL);
if (!Xdisplay)
{
fatalError("Couldn't connect to X server\n");
}
Xscreen = DefaultScreen(Xdisplay);
Xroot = RootWindow(Xdisplay, Xscreen) ;
fbconfigs = glXChooseFBConfig(Xdisplay, Xscreen, VisData, &numfbconfigs);
for (int i = 0; i < numfbconfigs; i++)
{
visual = (XVisualInfo_CPP*) glXGetVisualFromFBConfig(Xdisplay,
fbconfigs[i]);
if (!visual)
continue;
pictFormat = XRenderFindVisualFormat(Xdisplay, visual->visual);
if (!pictFormat)
continue;
if (pictFormat->direct.alphaMask > 0)
{
fbconfig = fbconfigs[i];
break;
}
}
/* Create a colormap - only needed on some X clients, eg. IRIX */
cmap = XCreateColormap(Xdisplay, Xroot, visual->visual, AllocNone);
/* Prepare the attributes for our window */
attr.colormap = cmap;
attr.border_pixel = 0;
attr.event_mask = StructureNotifyMask | EnterWindowMask | LeaveWindowMask
| ExposureMask | ButtonPressMask | ButtonReleaseMask
| OwnerGrabButtonMask | KeyPressMask | KeyReleaseMask;
attr.background_pixmap = None;
attr_mask = CWBackPixmap | CWColormap | CWBorderPixel | CWEventMask; /* What's in the attr data */
width = DisplayWidth(Xdisplay, DefaultScreen(Xdisplay)) ;
height = DisplayHeight(Xdisplay, DefaultScreen(Xdisplay)) ;
x = width / 2, y = height / 2;
// x=0, y=10;
/* Create the window */
attr.do_not_propagate_mask = NoEventMask;
WindowHandle = XCreateWindow(Xdisplay, /* Screen */
Xroot, /* Parent */
x, y, width, height,/* Position */
1,/* Border */
visual->depth,/* Color depth*/
InputOutput,/* klass */
visual->visual,/* Visual */
attr_mask, &attr);/* Attributes*/
if (!WindowHandle)
{
fatalError("Couldn't create the window\n");
}
/* Configure it... (ok, ok, this next bit isn't "minimal") */
textprop.value = (unsigned char*) title;
textprop.encoding = XA_STRING;
textprop.format = 8;
textprop.nitems = strlen(title);
hints.x = x;
hints.y = y;
hints.width = width;
hints.height = height;
hints.flags = USPosition | USSize;
StartupState = XAllocWMHints();
StartupState->initial_state = NormalState;
StartupState->flags = StateHint;
XSetWMProperties(Xdisplay, WindowHandle, &textprop, &textprop,/* Window title/icon title*/
NULL, 0,/* Argv[], argc for program*/
&hints, /* Start position/size*/
StartupState,/* Iconised/not flag */
NULL);
XFree(StartupState);
/* Open it, wait for it to appear */
int event_base, error_base = 0;
XMapWindow(Xdisplay, WindowHandle);
// }
XIfEvent(Xdisplay, &event, WaitForMapNotify, (char*) &WindowHandle);
/* Set the kill atom so we get a message when the user tries to close the window */
if ((del_atom = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", 0)) != None)
{
XSetWMProtocols(Xdisplay, WindowHandle, &del_atom, 1);
}
}
Here are the settings for VisData:
static int VisData[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE,
GLX_WINDOW_BIT, GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, 1, GLX_GREEN_SIZE,
1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_DEPTH_SIZE, 1,
None
};
Here is where the rendering context is created:
static void createTheRenderContext() {
/* See if we can do OpenGL on this visual */
int dummy;
if (!glXQueryExtension(Xdisplay, &dummy, &dummy))
{
fatalError("OpenGL not supported by X server\n");
}
/* Create the OpenGL rendering context */
RenderContext = glXCreateNewContext(Xdisplay, fbconfig, GLX_RGBA_TYPE, 0,
True);
if (!RenderContext)
{
fatalError("Failed to create a GL context\n");
}
GLXWindowHandle = glXCreateWindow(Xdisplay, fbconfig, WindowHandle, NULL);
/* Make it current */
if (!glXMakeContextCurrent(Xdisplay, GLXWindowHandle, GLXWindowHandle,
RenderContext))
{
fatalError("glXMakeCurrent failed for window\n");
}
}
What ratchet freak suggestet (Aero Glass effect in Windows) does not happen by accident, because one has to manually enable DWM transparency for this to happen.
However in X11/GLX it is perfectly possible to end up with a visual mode that has an Alpha Channel by default. If you want to get realiably a window that does or does not have an alpha channel the code gets a bit more complex than what most toolkits do.
The code you're using looks strikingly familiar. To be specific it seems to originate from a codesample I wrote about how to create a transparent window (you see where this is going), namely this code:
https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/x11argb_opengl/x11argb_opengl.c
The key sequence is this:
fbconfigs = glXChooseFBConfig(Xdisplay, Xscreen, VisData, &numfbconfigs);
fbconfig = 0;
for(int i = 0; i<numfbconfigs; i++) {
visual = (XVisualInfo*) glXGetVisualFromFBConfig(Xdisplay, fbconfigs[i]);
if(!visual)
continue;
pict_format = XRenderFindVisualFormat(Xdisplay, visual->visual);
if(!pict_format)
continue;
fbconfig = fbconfigs[i];
if(pict_format->direct.alphaMask > 0) {
break;
}
}
What this does is, it selects an X11 Visual that matches one of the previously selected FBConfigs that also contains an alpha mask.
If I had to make a bet I suspect that the VisData array you passed to glXChooseFBConfig does not specify an alpha channel. So what happens is, that you may end up with a window that has an X11 alpha mask, but not an alpha channel accessible to OpenGL.
Since I never intended that code to be used for windows that don't have an alpha channel this code does only whats originally intended if VisData does select for an alpha channel.
You have now two options:
implement a complementary test if(pict_format->direct.alphaMask == 0 && no_alpha_in(VisData)) break;
select for an alpha channel in VisData and clear the alpha channel to 1.0 with OpenGL glClearColor(…,…,…,1.0f);
This is not a opengl problem, but rather the kind of window you are creating. I suspect you running a window manager with supports transparency effects. Either way, what probably is happening is that, when you render the transparent poly, the window canvas ends up with some alpha, and your window manager assumes that you want the background transparent. Turn off all advanced effects of your window manager to check.
I am not familiar with window creation code using xlib, but it probably has to do with the kind of window you are creating.

GL/glx isn't linking correctly

System Specs and task
I am using Code::Blocks on Ubuntu 10.10 and playing around with OpenGL and glx. I'm in the process of learning C++(from a background in C and Java), so the style of any code doesn't conform to any real good standards (but I'm open to suggestions on how to improve, even if you don't have an answer to the question)
Edit:
Huge Realization: The default OpenGL Project Code::Blocks creates is C, not C++. I'm looking into this now.
I'm currently trying to modify the default OpenGL project on Code::Blocks into a simple 3d engine. I am currently getting the error:
expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before 'Draw'
Which disappears as soon as I comment out the #include for < GL/glx.h>
I read on a forum somewhere that Code::Blocks doesn't look in usr/include/ by default, but I added that to the search directories for the compiler in the project build options and it didn't seem to fix anything.
Code:
main.cpp: main.c:
#include <time.h>
#include "Draw.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
/*draw init here*/
Draw::Draw renderer = Draw::Draw.getDraw();
printf( "Press left mouse button to rotate around X axis\n" );
printf( "Press middle mouse button to rotate around Y axis\n" );
printf( "Press right mouse button to rotate around Z axis\n" );
printf( "Press ESC to quit the application\n" );
/* timing variable*/
/* Set it to delay half a second before rendering the first frame*/
clock_t flip_time = clock() + 0.5f * CLOCKS_PER_SEC;
while (1)
{
/* Update models */
/* Draw scene */
/* wait until it's been 1/60th of a second*/
while(clock() < flip_time){}
flip_time = clock() + (1.0f/60.0f) * CLOCKS_PER_SEC;
/* Actually flip the frame */
}
}
Draw.h:
#ifndef DRAW_H
#define DRAW_H
#include <GL/glx.h> /* This is the problem line */
#include <GL/gl.h>
#include <X11/X.h> /* X11 constant (e.g. TrueColor) */
#include <X11/keysym.h>
class Draw
{
public:
static Draw getDraw();
virtual ~Draw();
void update();
void render();
protected:
private:
Draw();
bool init();
/* The singleton*/
static Draw *instance;
static bool exists;
/* X Window values */
Display *dpy;
Window win;
GLboolean doubleBuffer;
/* X Parameters*/
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
GLXContext cx;
XEvent event;
int dummy;
};
#endif // DRAW_H
Last, but not least Draw.cpp:
#include "Draw.h"
/* Set up the singleton*/
bool Draw::exists = false;
Draw* Draw::instance = NULL;
Draw::Draw()
{
/*TODO: make this constructor */
}
Draw::~Draw()
{
//dtor
}
Draw Draw::getDraw()
{
if(!exists)
{
instance = new Draw();
instance->init();
exists = true; //Thanks mat, This line was accidentally removed with extraneous comments
}
return *instance;
}
bool Draw::init()
{
/* Get the buffers ready */
static int snglBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, None};
static int dblBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None};
/* Double Buffered is best*/
doubleBuffer = GL_TRUE;
/*TODO: add constructor if it hasn't been constructed already*/
dpy = XOpenDisplay(NULL);
if (dpy == NULL)
{
return false;
}
/* make sure OpenGL's GLX extension supported */
if(!glXQueryExtension(dpy, &dummy, &dummy))
{
return false;
}
/* find an appropriate visual */
/* find an OpenGL-capable RGB visual with depth buffer */
vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
if (vi == NULL)
{
vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
if (vi == NULL)
{
return false;
}
doubleBuffer = GL_FALSE;
}
/*
TODO: Fix or remove this
if(vi->class != TrueColor)
{
return false;
}
*/
/* create an OpenGL rendering context */
/* create an OpenGL rendering context */
cx = glXCreateContext(dpy, vi, /* no shared dlists */ None,
/* direct rendering if possible */ GL_TRUE);
if (cx == NULL)
{
return false;
}
/* create an X window with the selected visual */
/* create an X colormap since probably not using default visual */
cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
swa.colormap = cmap;
swa.border_pixel = 0;
swa.event_mask = KeyPressMask | ExposureMask
| ButtonPressMask | StructureNotifyMask;
win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0,
300, 300, 0, vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask, &swa);
XSetStandardProperties(dpy, win, "main", "main", None,
NULL, NULL, NULL);
/* bind the rendering context to the window */
glXMakeCurrent(dpy, win, cx);
/* request the X window to be displayed on the screen */
XMapWindow(dpy, win);
/* configure the OpenGL context for rendering */
glEnable(GL_DEPTH_TEST); /* enable depth buffering */
glDepthFunc(GL_LESS); /* pedantic, GL_LESS is the default */
glClearDepth(1.0); /* pedantic, 1.0 is the default */
/* frame buffer clears should be to black */
glClearColor(0.0, 0.0, 0.0, 0.0);
/* set up projection transform */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);
/* establish initial viewport */
/* pedantic, full window size is default viewport */
glViewport(0, 0, 300, 300);
return true;
}
void Draw::update()
{
/*TODO: Add things to draw here*/
}
void Draw::render()
{
/* actually flip buffers here */
}
I removed a ton of comments before posting this here, but that shouldn't affect whether or not it compiles.
Thanks!
This line in your main file is wrong:
Draw::Draw renderer = Draw::Draw.getDraw();
Draw::Draw is not a type. To get this to compile, you just need:
Draw renderer = Draw.getDraw();
It looks like you're trying to build a singleton. You code does not do that at all, you'll get a copy each time. (Note that you're not setting exists anywhere, but that's just an extra bug.) You should be returning a pointer or a reference to the shared instance. See for instance this article to get the syntax right: C++ Singleton design pattern.
I found the issue with the linking.
The default project for OpenGL in Code::Blocks is NOT C++, it's C. I configured it to use g++ and it fixed the issue with glx not linking in correctly. I revised my singleton to look a little more like this and it works correctly now too. I have an issue now with the window not appearing, but I should be able to figure that out.
Thanks!

how to find transformation for static mesh?

How to find transformation for static mesh?
I'll be using it for my picking function.
I have tried this but it didn't work...
void StaticMesh::Render(void)
{
D3DXMATRIX matWorld;
D3DXMatrixIdentity(&matWorld);
device->SetTransform(D3DTS_WORLD, &matWorld);
for(DWORD i = 0; i < numMaterials; i++) // loop through each subset
{
device->SetMaterial(&material[i]); // set the material for the subset
device->SetTexture(0, texture[i]); // ...then set the texture
mesh->DrawSubset(i); // draw the subset
}
s = &matWorld; // THIS WOULD BE THE TRANSFORMATION FOR THE OBJECT
}
thanks in advance.
Edit 2:
Here is full class
#include "StdAfx.h"
#include "StaticMesh.h"
StaticMesh::StaticMesh(LPDIRECT3DDEVICE9* dev)
{
d3ddev=dev;
device = *d3ddev;
}
StaticMesh::~StaticMesh(void)
{
}
void StaticMesh::Render(void)
{
LPDIRECT3DDEVICE9 device=*d3ddev;
D3DXMATRIX matWorld;
D3DXMatrixIdentity(&matWorld);
device->SetTransform(D3DTS_WORLD, &matWorld);
for(DWORD i = 0; i < numMaterials; i++) // loop through each subset
{
device->SetMaterial(&material[i]); // set the material for the subset
device->SetTexture(0, texture[i]); // ...then set the texture
mesh->DrawSubset(i); // draw the subset
}
s = matWorld;
}
StaticMesh* StaticMesh::LoadXFile(LPCWSTR fileName, LPDIRECT3DDEVICE9* dev)
{
StaticMesh *obj = this;
obj = new StaticMesh(dev);
obj->Load(fileName);
return obj;
}
void StaticMesh::Load(LPCWSTR fileName)
{
D3DXLoadMeshFromX(fileName, // load this file
D3DXMESH_SYSTEMMEM, // load the mesh into system memory
device, // the Direct3D Device
NULL, // we aren't using adjacency
&bufMeshMaterial, // put the materials here
NULL, // we aren't using effect instances
&numMaterials, // the number of materials in this model
&mesh); // put the mesh here
// retrieve the pointer to the buffer containing the material information
D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufMeshMaterial->GetBufferPointer();
// create a new material buffer and texture for each material in the mesh
material = new D3DMATERIAL9[numMaterials];
texture = new LPDIRECT3DTEXTURE9[numMaterials];
for(DWORD i = 0; i < numMaterials; i++) // for each material...
{
// Copy the material
material[i] = tempMaterials[i].MatD3D;
// Set the ambient color for the material (D3DX does not do this)
material[i].Ambient = material[i].Diffuse;
// Create the texture if it exists - it may not
texture[i] = NULL;
if (tempMaterials[i].pTextureFilename)
{
D3DXCreateTextureFromFileA(device, tempMaterials[i].pTextureFilename,&texture[i]);
}
}
}
void StaticMesh::CleanUp(void)
{
mesh->Release();
}
EDIT 3:
void GUIDialog::Picking(HWND hWnd, int status)
{
LPDIRECT3DDEVICE9 device = *d3ddev;
D3DXMATRIX matProj;
POINT pt;
D3DVIEWPORT9 vp;
D3DXMATRIX *matWorld=NULL;
D3DXMATRIX matView;
GetCursorPos(&pt);
ScreenToClient(hWnd, &pt);
device->GetTransform(D3DTS_PROJECTION, &matProj);
device->GetViewport(&vp);
device->GetTransform(D3DTS_VIEW, &matView);
for(int i=0; (int)edit.size() > i; i++)
{
matWorld=&edit.at(i)->staticMesh->s;
// Use inverse of matrix
D3DXVECTOR3 rayPos((float)pt.x, (float)pt.y,0); // near-plane position
D3DXVECTOR3 rayDir((float)pt.x, (float)pt.y,1); // far-plane position
D3DXVec3Unproject(&rayPos,&rayPos,&vp,&matProj,&matView,matWorld);
D3DXVec3Unproject(&rayDir,&rayDir,&vp,&matProj,&matView,matWorld);
rayDir -= rayPos; // make a direction from the 2 positions
D3DXVec3Normalize(&rayDir,&rayDir);
if(FAILED(D3DXIntersect(edit.at(i)->staticMesh->mesh, &rayPos, &rayDir, &edit.at(i)->staticMesh->hasHit, NULL, NULL, NULL, &edit.at(i)->staticMesh->
distanceToCollision, NULL, NULL)))
{
PostQuitMessage(0);
};
if(edit.at(i)->staticMesh->hasHit!=0&&status==WM_LBUTTONUP)
{
if(status==WM_LBUTTONUP)
EventProc(HIT, *edit.at(i));
}
}
}
Well your code above is correct matWorld IS the transform of the object. If you wish to transform a ray into object local space (ie to the space prior to the matWorld transformation) then you simply need to multiply your world space ray by the inverse of matWorld ...