selection - OpenGL ES 2.0 Object Picking on iOS -
what best method select objects have been drawn in opengl es 2.0 (ios)?
i drawing points.
here working prototype of color picking, tested on old ipads , working well. part of project called incube chess 1 may find in app store. main code see located in class derived glkviewcontroller this:
@interface incubeviewcontroller : glkviewcontroller
this means have glkview in it: ((glkview *)self.view).
here properties:
@property (strong, nonatomic) eaglcontext *context; @property (strong, nonatomic) glkbaseeffect *effect;
don't forget synthesize them in *.m file.
@synthesize context = _context; @synthesize effect = _effect;
the idea have chess pieces on table (or objects in 3d scene) , need find piece in list of pieces tapping on screen. is, need convert 2d screen tap coords (@point in case) chess piece instance.
each piece has unique id call "seal". can allocate seals from 1 something. selection function returns piece seal found tap coords. having seal can find piece in pieces hash table or array in way this:
-(piece *)findpiecebyseal:(gluint)seal { /* !!! black background in off screen buffer produces 0 seals. allows filter out taps did not select (will mentioned below) !!! */ if (seal == 0) return nil; pieceseal *sealkey = [[pieceseal alloc] init:s]; piece *p = [sealhash objectforkey:sealkey]; [sealkey release]; return p; }
"sealhash" nsmutabledictionary.
now main selection function. note, glkview antialised , can't use buffers color picking. mean need create own off screen buffer antialiasing disabled picking purposes only.
- (nsuinteger)findsealbypoint:(cgpoint)point { nsinteger height = ((glkview *)self.view).drawableheight; nsinteger width = ((glkview *)self.view).drawablewidth; byte pixelcolor[4] = {0,}; gluint colorrenderbuffer; gluint framebuffer; glgenframebuffers(1, &framebuffer); glbindframebuffer(gl_framebuffer, framebuffer); glgenrenderbuffers(1, &colorrenderbuffer); glbindrenderbuffer(gl_renderbuffer, colorrenderbuffer); glrenderbufferstorage(gl_renderbuffer, gl_rgba8_oes, width, height); glframebufferrenderbuffer(gl_framebuffer, gl_color_attachment0_oes, gl_renderbuffer, colorrenderbuffer); glenum status = glcheckframebufferstatus(gl_framebuffer); if (status != gl_framebuffer_complete) { nslog(@"framebuffer status: %x", (int)status); return 0; } [self render:dm_select]; cgfloat scale = uiscreen.mainscreen.scale; glreadpixels(point.x * scale, (height - (point.y * scale)), 1, 1, gl_rgba, gl_unsigned_byte, pixelcolor); gldeleterenderbuffers(1, &colorrenderbuffer); gldeleteframebuffers(1, &framebuffer); return pixelcolor[0]; }
note function takes account display scale (retina or new ipads).
here render() function used in function above. note, rendering purposes clear s buffer background color , selecting case makes black can check if tapped on piece @ or not.
- (void) render:(drawmode)mode { if (mode == dm_render) glclearcolor(backgroundcolor.r, backgroundcolor.g, backgroundcolor.b, 1.0f); else glclearcolor(0.0f, 0.0f, 0.0f, 1.0f); glclear(gl_color_buffer_bit | gl_depth_buffer_bit); /* draw pieces. */ (int = 0; < [model->pieces count]; i++) { piece *p = [model->pieces objectatindex:i]; [self drawpiece:p mode:mode]; } }
next how draw piece.
- (void) drawpiece:(piece *)p mode:(drawmode)mode { piecetype type; [self pushmatrix]; glkmatrix4 modelviewmatrix = self.effect.transform.modelviewmatrix; glkmatrix4 translatematrix = glkmatrix4maketranslation(p->drawpos.x, p->drawpos.y, p->drawpos.z); modelviewmatrix = glkmatrix4multiply(modelviewmatrix, translatematrix); glkmatrix4 rotatematrix; glkmatrix4 scalematrix; if (mode == dm_render) { scalematrix = glkmatrix4makescale(p->scale.x, p->scale.y, p->scale.z); } else { /* !!! make piece bit bigger in off screen buffer selection purposes sure tapped correctly finger.*/ scalematrix = glkmatrix4makescale(p->scale.x + 0.2, p->scale.y + 0.2, p->scale.z + 0.2); } modelviewmatrix = glkmatrix4multiply(modelviewmatrix, scalematrix); self.effect.transform.modelviewmatrix = modelviewmatrix; type = p->type; if (mode == dm_render) { /* !!! use real pieces color , light on normal drawing !!! */ glkvector4 color[pclast] = { [pcwhite] = whitescolor, [pcblack] = blackscolor }; self.effect.constantcolor = color[p->color]; self.effect.light0.enabled = gl_true; } else { /* !!! use piece seal color. important turn light off !!! */ self.effect.light0.enabled = gl_false; self.effect.constantcolor = glkvector4make(p->seal / 255.0f, 0.0f, 0.0f, 0.0f); } /* normal render piece using geometry buffers. */ [self renderpiece:type]; [self popmatrix]; }
this how use functions shown above.
- (ibaction) tapgesture:(id)sender { if ([(uitapgesturerecognizer *)sender state] == uigesturerecognizerstateended) { cgpoint tap = [(uitapgesturerecognizer *)sender locationinview:self.view]; piece *p = [self findpiecebyseal:[self findsealbypoint:tap]]; /* !!! selected object !!! */ } }
this it. have precise picking algorithm better ray tracing or others.
here helpers push/pop matrix things.
- (void)pushmatrix { assert(matrixsp < sizeof(matrixstack) / sizeof(glkmatrix4)); matrixstack[matrixsp++] = self.effect.transform.modelviewmatrix; } - (void)popmatrix { assert(matrixsp > 0); self.effect.transform.modelviewmatrix = matrixstack[--matrixsp]; }
here glkview setup/cleanup functions used.
- (void)viewdidload { [super viewdidload]; self.context = [[[eaglcontext alloc] initwithapi:keaglrenderingapiopengles2] autorelease]; if (!self.context) nslog(@"failed create es context"); glkview *view = (glkview *)self.view; view.context = self.context; view.drawabledepthformat = glkviewdrawabledepthformat24; [self setupgl]; } - (void)viewdidunload { [super viewdidunload]; [self teardowngl]; if ([eaglcontext currentcontext] == self.context) [eaglcontext setcurrentcontext:nil]; self.context = nil; } - (void)setupgl { [eaglcontext setcurrentcontext:self.context]; self.effect = [[[glkbaseeffect alloc] init] autorelease]; if (self.effect) { self.effect.useconstantcolor = gl_true; self.effect.colormaterialenabled = gl_true; self.effect.light0.enabled = gl_true; self.effect.light0.diffusecolor = glkvector4make(1.0f, 1.0f, 1.0f, 1.0f); } /* !!! draw antialiased geometry !!! */ ((glkview *)self.view).drawablemultisample = glkviewdrawablemultisample4x; self.pauseonwillresignactive = yes; self.resumeondidbecomeactive = yes; self.preferredframespersecond = 30; gldisable(gl_dither); glenable(gl_cull_face); glenable(gl_depth_test); gllinewidth(2.0f); /* load pieces geometry */ [self loadgeometry]; } - (void)teardowngl { drawready = no; [eaglcontext setcurrentcontext:self.context]; [self unloadgeometry]; }
hope helps , may closes "the picking question" forever :)
Comments
Post a Comment