/usr/share/psychtoolbox-3/PsychGLImageProcessing/moglExtractTexture.m is in psychtoolbox-3-common 3.0.11.20131230.dfsg1-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 | function varargout = moglExtractTexture(cmd, varargin)
% moglExtractTexture(cmd [, arg1][, arg2][, ...]) - "MOGL Video texture extraction"
%
%
% The algorithm makes heavy use of GPU based image processing for maximum
% speed, so it needs at least NVidia Geforce 6000 series or ATI Radeon
% X1000 series graphics hardware (and any later models or equivalent
% hardware) to work. It also needs the PTB imaging pipeline enabled, at
% least fast offscreen window support. You do this, e.g., by replacing a
% call to ...
%
% [win, winRect] = Screen('OpenWindow', screenid, 0);
%
% ... with a call sequence like this ...
%
% PsychImaging('PrepareConfiguration');
% PsychImaging('AddTask', 'General', 'UseFastOffscreenWindows');
% [win , winRect] = PsychImaging('OpenWindow', screenid, 0);
%
%
%
% Subcommands, their syntax & meaning:
% ====================================
%
% [oldflag, oldgain] = moglExtractTexture('DebugFlag', flag [, debugGain]);
% - Set debug flag to value 'flag'. Default is zero. Non-zero values enable
% different visualizations that may aid debugging non-working setups. 1 =
% Show silhouette buffer, 2 = Show trackbuffer, 3 = Show extracted texture.
% A setting of -1 shows the real rendered image. A value of -2 disables any
% kind of textual warnings.
%
% The optional 'debugGain' parameter must be a 4 component [R G B A] color
% vector with modulation gains for the drawn "debug images" - simply to
% scale each color channel in intensity to allow for display of values
% outside the standard displayable range between zero and one.
%
%
% context = moglExtractTexture('CreateContext', window, rect, texCoordMin, texCoordMax, texResolution [,zThreshold=Off]);
% - Create a "rendercontext" for a single 3D object. Returns a 'context'
% handle to it which needs to be passed in to all other functions as
% reference. All following parameters are required and don't have any
% defaults:
%
% 'window' Handle of masterwindow - The onscreen window used for rendering.
% This is not neccessarily the window to which final stimulus will be drawn
% to, but it is needed as a "parent" for all ressources.
%
% 'rect' A Psychtoolbox rectangle [left top right bottom] that describes
% the size and shape of the input video texture. This rect must have the
% same size as the input video image textures -- Lots of internal
% calculations depend on this geometry spec!
%
% 'texCoordMin' Two element vector which contains the minimum texture
% coordinate values contained in the 3D scene for x- resp. y-direction.
%
% 'texCoordMax' Two element vector which contains the maximum texture
% coordinate values contained in the 3D scene for x- resp. y-direction.
%
% 'texResolution' Two element vector which contains the internal resolution
% for x- resp. y-direction of the 3D object surface. Higher values mean finer
% resolution and less aliasing, but also higher storage requirements and
% longer processing times. This defines the size of returned extracted
% textures.
%
% 'zThreshold' Optional zThreshold for occlusion test: By default, it is
% 10.0 ie. occlusion test disabled. A value between 0.0 and 1.0 will enable
% occlusion testing -- Texels that would correspond to occluded surface patches are
% not extracted. Small numbers (close to zero) make the test more sensitive but
% can cause artifacts due to internal numeric roundoff errors. Bigger
% numbers (closer to one) make it more robust but less powerful. The
% "sweet-spot" depends on your hardware and 3D scene. Empirically a setting
% of 0.0001 is a good value for ATI Radeon X1000 series hardware.
% The default setting (bigger than 1.0) will disable occlusion test --
% "Hidden texels" are not ignored, but updated with bogus extracted texture.
%
%
% context = moglExtractTexture('SetRenderCallback', context, callbackEvalString);
% - Define the 'eval' string for this context to be used as rendercallback.
% Pass in a Matlab command string (for evaluation via eval() function in the
% Workspace of the calling function). This string is called/executed during
% each 'Update' cycle. It has to contain the code that performs the actual
% rendering of the 3D scene or object.
%
% The called rendering code *must not* glClear() the framebuffer or mess
% around with alpha-blending state or depth-buffer/depth-test settings, nor
% should it bind any shaders! It makes sense to disable any kind of
% lighting or texture mapping, as no photorealistic image is rendered, so
% it would be a waste of computation time.
%
%
% context = moglExtractTexture('DestroyContext', context);
% - Destroy a processing context, release all of its ressources.
%
%
% [texBuffer, texId, texTarget] = moglExtractTexture('Extract', context, inputTexture [, newTexture = 0]);
% - Perform an 'Extract' cycle for given context. A new "3D frame" is rendered
% via the rendercallback function, then analysed, to provide the 3D surface
% geometry and occlusion info and mapping for texture extraction. This info
% is then used to extract pixel color values from the given video input
% texture 'inputTexture' and the final extracted texturemap is stored
% inside an internal texture buffer. A handle to that internal buffer
% 'texBuffer' is returned. The handle is owned by this function! You should
% not close or otherwise mess with the provided buffer. You can read the
% final texture from it, acquire a temporary OpenGL texture handle to it
% for texture mapping, etc. You are even allowed to perform destructive
% write informations on the buffer to change its pixel content. But do not
% destory and reallocate the buffer, change its size, number of layers,
% resolution or any other property! For your convenience, 'texId' and
% 'texTarget' also provide standard OpenGL handles to texture id and
% target, associated with 'texBuffer'.
%
% Alternatively you can set the optional flag 'newTexture' to a value of 1.
% In that case, a new extracted texture 'texBuffer' is returned and you own
% this texture, ie., you can do with it whatever you want and you are
% responsible for releasing the texture via Screen('Close', texBuffer);
% once you are done with it.
%
% History:
% 05/10/09 Initial implementation, derived from moglFDF (MK).
% Need OpenGL constants:
global GL;
% Internal state:
global moglExtractTexture_OriginalContext;
persistent contextcount;
persistent debug;
persistent debugGain;
if nargin < 1
error('You must provide a "cmd" subcommand to execute!');
end
if isempty(contextcount)
contextcount = 0;
moglExtractTexture_OriginalContext = [];
debug = 0;
if isempty(GL)
% If OpenGL not initialized, do a full init for 3D mode:
error('OpenGL mode not initialized! You *must* call InitializeMatlabOpenGL before the first call to this routine or any Screen() function!')
end
end
% Subcommand dispatch:
% Initialization of a new context: Allocate and setup all ressources:
if strcmpi(cmd, 'CreateContext')
% Fetch all arguments - They are all required.
if nargin < 6
error(sprintf('Some mandatory input arguments to "%s" are missing. Please provide them!', cmd)); %#ok<SPERR>
end
createContext = 1;
% First time init? I.e. is this the first context to be created?
if contextcount == 0
% Yes. Perform all one-time initialization work and create a
% template context from which all other contexts can be derived:
end
% Type of expected first argument depends if this is a 'CreateContext'
% call or a 'ReinitContext' call:
if createContext
% Parent window: Provides OpenGL master-/slave- contexts for our
% operations, shaders and buffers, as well as reference for
% rendertarget size:
ctx.parentWin = varargin{1};
if ~isscalar(ctx.parentWin) || ~ismember(ctx.parentWin, Screen('Windows'))
disp(ctx.parentWin);
error('Invalid "window" argument provided to "CreateContext" - No such window (see above)!');
end
else
% Old 'ctx' handle of context to reparameterize / reinit:
ctx = varargin{1};
% Make sure we've got a valid handle:
if ~isstruct(ctx)
disp(ctx);
error('Invalid "context" argument provided to "ReinitContext" - This is not a valid moglExtractTexture context handle!');
end
if ~isfield(ctx, 'moglExtractTextureMagic')
disp(ctx);
error('Invalid "context" argument provided to "ReinitContext" - This is not a valid moglExtractTexture context handle!');
end
% Release all buffers, but not the shaders!
% Delete all offscreen windows, gloperators and buffers like IBO's
% VBO's, PBO's, FBO's etc, so they can get recreated, based on the
% new context parameters:
deleteContextBuffers(ctx);
RestoreGL;
% Decrement contextcount, so it can be reincremented at end of this
% function:
contextcount = max(contextcount - 1, 0);
end
% Assign our magic cookie...
ctx.moglExtractTextureMagic = 'Funky magic-cookie';
% Get all other arguments and perform parameter type and range checks:
ctx.rect = varargin{2};
if ~isnumeric(ctx.rect) || length(ctx.rect)~=4
disp(ctx.rect);
error('Invalid "rect" argument provided to "CreateContext" - Must be a 4 component vector that describes the size and shape of the input video rectangle [left top right bottom]');
end
ctx.rect = double(ctx.rect);
if IsEmptyRect(ctx.rect)
disp(ctx.rect);
error('Invalid "rect" argument provided to "CreateContext" - Must be a non-empty rect that describes the size and shape of the input video rectangle [left top right bottom]');
end
ctx.texCoordMin = varargin{3};
if ~isnumeric(ctx.texCoordMin) || length(ctx.texCoordMin)~=2
disp(ctx.texCoordMin);
error('Invalid "texCoordMin" argument provided to "CreateContext" - Must be a 2 component vector of minimal texture coordinates in x- and y- direction!');
end
ctx.texCoordMax = varargin{4};
if ~isnumeric(ctx.texCoordMax) || length(ctx.texCoordMax)~=2
disp(ctx.texCoordMax);
error('Invalid "texCoordMax" argument provided to "CreateContext" - Must be a 2 component vector of maximal texture coordinates in x- and y- direction!');
end
ctx.texResolution = varargin{5};
if ~isnumeric(ctx.texResolution) || length(ctx.texResolution)~=2
disp(ctx.texResolution);
error('Invalid "texResolution" argument provided to "CreateContext" - Must be a 2 component vector of integral numbers with processing resolution in x- and y- direction!');
end
if round(ctx.texResolution) ~= ctx.texResolution
disp(ctx.texResolution);
error('Invalid "texResolution" argument provided to "CreateContext" - Must be integral numbers for resolution in x- and y- direction!');
end
if min(ctx.texResolution) < 2
disp(ctx.texResolution);
error('Invalid "texResolution" argument provided to "CreateContext" - Each component must be at least 2 units!');
end
% Basic checks passed: Now check for inter-parameter consistency:
if nargin >= 7
ctx.zThreshold = varargin{6};
if ~isscalar(ctx.zThreshold)
disp(ctx.zThreshold);
error('Invalid "zThreshold" argument provided to "CreateContext" - Must be a positive number in range 0.0 - 1.0 for z-Test, and bigger for z-Test disabled!');
end
else
% Default: z-Test disabled:
ctx.zThreshold = 10.0;
end
% Backup current GL context binding:
BackupGL;
% Make sure our Screen context is active:
SwitchToPTB;
% Retrieve info about our hosting window. This will implicitely enable
% our parents OpenGL context, so we can do GL query commands safely:
winfo = Screen('GetWindowInfo', ctx.parentWin);
% Retrieve maximum width or height of textures and offscreen windows
% supported by this GL implementation:
maxtexsize = glGetIntegerv(GL.MAX_RECTANGLE_TEXTURE_SIZE_EXT);
% Check requested internal resolution against hw-limit:
if max(ctx.texResolution) > maxtexsize
disp(ctx.texResolution)
error(sprintf('Requested "texResolution" parameter too big in at least one dimension - Your graphics card can not handle that! Maximum is %i\n', maxtexsize)); %#ok<SPERR>
end
% Need these checks only on original context creation:
if createContext
% Imaging pipeline active in at least minimum configuration?
if ~bitand(winfo.ImagingMode, mor(kPsychNeedFastBackingStore, kPsychNeedFastOffscreenWindows))
% Neither basic pipeline, nor fast offscreen window support
% activated in parent window. This is a no-go!
error('In "CreateContext": The PTB imaging pipeline is not active for provided parent window - this will not work! Need at least support for fast offscreen windows.');
end
if winfo.GLSupportsFBOUpToBpc < 32 || winfo.GLSupportsTexturesUpToBpc < 32
error('In "CreateContext": Your gfx-hardware is not capable of handling textures and buffers with the required precision - this function will not work on your hardware!');
end
% Check for all required extensions:
if ~( ~isempty(findstr(glGetString(GL.EXTENSIONS), '_framebuffer_object')) && ...
~isempty(findstr(glGetString(GL.EXTENSIONS), 'GL_ARB_shading_language')) && ...
~isempty(findstr(glGetString(GL.EXTENSIONS), 'GL_ARB_shader_objects')) && ...
~isempty(findstr(glGetString(GL.EXTENSIONS), 'GL_ARB_fragment_shader')) && ...
~isempty(findstr(glGetString(GL.EXTENSIONS), 'GL_ARB_vertex_shader')) && ...
(~isempty(findstr(glGetString(GL.EXTENSIONS), 'GL_APPLE_float_pixels')) || ...
~isempty(findstr(glGetString(GL.EXTENSIONS), '_color_buffer_float'))))
% At least one of the required extensions is missing!
error('In "CreateContext": Your gfx-hardware does not support all required OpenGL extensions - this function will not work on your hardware!');
end
end
% Ok, all checks passed.
% Create all relevant FBO buffers, aka Offscreen windows:
% Silhouette buffer: Contains the "perspective correct image space"
% image of the rendered 3D object. The object is rendered "normally" in
% a first render pass to get its silhouette, except that the color of
% each rendered pixel is not a shaded/lit color, but its encoded
% interpolated surface texture coordinate. This will be done by a
% proper fragment shader during render pass.
%
% The buffer is an FBO backed offscreen window the same size as the
% parent window, but with 32bpc float format to store accurate texture
% coordinates in the pixel colors. Red channel encodes s-coord, Green
% channel encodes t-coord of 2D texture coordinate, blue encodes a
% foreground/background flag, alpha encodes z-buffer depths.
[ctx.silhouetteWidth, ctx.silhouetteHeight] = RectSize(ctx.rect);
ctx.silhouetteBuffer = Screen('OpenOffscreenWindow', ctx.parentWin, [0 0 0 0], [0, 0, ctx.silhouetteWidth, ctx.silhouetteHeight], 128, 32);
% Retrieve OpenGL texture handle for the sihouetteBuffer:
ctx.silhouetteTexture = Screen('GetOpenGLTexture', ctx.parentWin, ctx.silhouetteBuffer);
% Tracking buffer: Contains the unwarped/flattened image of the 3D
% objects surface, created by a 2nd rendering pass of the 3D object,
% but with special vertex-/fragment shaders attached.
%
% Pixel location (x,y) encodes for object surface texture coordinate
% (s,t): Its R, G and B channels encode interpolated X, Y, Z coordinate
% of the unwarped object surface. This way a lookup at position (s,t)
% provides the image space 3D coordinate of surface point (s,t) in
% "normal" camera centered and projected (X,Y,Z) space -- (X,Y) are
% projected image coordinates, (Z) is depths component. This allows to
% lookup the (X,Y) image position of a texel in the objects surface.
%
% Again a 32bpc float offscreen window FBO, but the resolution is
% chosen per user spec to be fine enough in texture coordinate space to
% match the size of the extracted texture map:
ctx.trackingBuffer = Screen('OpenOffscreenWindow', ctx.parentWin, [0 0 0 0], [0, 0, ctx.texResolution(1), ctx.texResolution(2)], 128, 32);
% Final buffer with extraced texture image. This one will get filled
% by the texture extraction shader. A RGBA8 texture format is
% sufficient, therefore we allocate a standard 32 bit surface:
ctx.OutTextureBuffer = Screen('OpenOffscreenWindow', ctx.parentWin, [0 0 0 0], [0, 0, ctx.texResolution(1), ctx.texResolution(2)], 32, 32);
% Load all our shaders - Need to do this only on original context
% creation, as shaders are recycled across context reinits. However, we
% can only recycle shaders from one existing context, not across
% different contexts, because each shader object also encapsulates
% per-context state like the settings of all Uniforms etc. and we can't
% share these!
if createContext
% Basepath to shaders:
% shaderpath = [fileparts(mfilename('fullpath')) filesep ];
shaderpath = '';
% Shader for 1st object renderpass: Encode texcoords and depths into
% color channel -- to fill silhouetteBuffer:
ctx.silhouetteRenderShader = LoadGLSLProgramFromFiles([shaderpath 'moglFDFSilhouetteRenderShader'], 1);
% Shader for 2nd object renderpass: Fill trackingBuffer
ctx.trackingRenderShader = LoadGLSLProgramFromFiles([shaderpath 'moglFDFTrackingRenderShader'], 1);
% Shader for final creation of foreground dots VBO spec from
% distribution in sampleBuffer and trackingBuffer:
ctx.textureExtractionShader = LoadGLSLProgramFromFiles([shaderpath 'moglTextureExtractionShader'], 1);
end
% Setup trackingRenderShader:
glUseProgram(ctx.trackingRenderShader)
% Compute texture coordinate offset and multiplier to apply in order to
% remap the real texture coordinate range into the normalized [-1:+1]
% interval that can pass through vertex clipping:
glUniform4f(glGetUniformLocation(ctx.trackingRenderShader, 'TextureOffsetBias'), ctx.texCoordMin(1), ctx.texCoordMin(2), 2.0/(ctx.texCoordMax(1) - ctx.texCoordMin(1)), 2.0/(ctx.texCoordMax(2) - ctx.texCoordMin(2)));
% Set viewport dimensions:
glUniform4f(glGetUniformLocation(ctx.trackingRenderShader, 'Viewport'), 0, 0, ctx.silhouetteWidth/2, ctx.silhouetteHeight/2);
% Setup shader for final texture extraction from input image and
% xformed geometry and silhouette:
glUseProgram(ctx.textureExtractionShader)
% Bind texunit 0 to object coordinates texture:
glUniform1i(glGetUniformLocation(ctx.textureExtractionShader, 'GeometryBuffer'), 0);
% Bind texunit 1 to input image texture:
glUniform1i(glGetUniformLocation(ctx.textureExtractionShader, 'InputImage'), 1);
% Bind texunit 2 to silhouette texture for last rendered frame:
glUniform1i(glGetUniformLocation(ctx.textureExtractionShader, 'Silhouette'), 2);
% Assign zThreshold for depths testing of foreground dots before
% output to handle occlusions correctly:
glUniform1f(glGetUniformLocation(ctx.textureExtractionShader, 'zThreshold'), ctx.zThreshold);
% Assign height of final output window + 1 to allow shader to invert
% y-coordinate of final dots properly to account for difference in
% y-axis direction of Screen()'s reference frame vs. OpenGL default
% frame:
% TODO FIXME: Needed or not?
glUniform1f(glGetUniformLocation(ctx.textureExtractionShader, 'ViewportHeight'), ctx.silhouetteHeight + 1);
% Define size of GeometryBuffer -- wrapAround values for interpolated texture lookup coordinates:
glUniform2f(glGetUniformLocation(ctx.textureExtractionShader, 'texWrapAround'), ctx.texResolution(1), ctx.texResolution(2));
% Define inverse remapping of texture coordinates into range
% 0-texResolution -- The size of the trackingBuffer. N.B.: A neutral
% mapping would be (0, 0, 1, 1) - That would pass trackingBuffer
% texture coordinates instead of object texture coordinates.
% TODO delete glUniform4f(glGetUniformLocation(ctx.textureExtractionShader, 'TextureOffsetBias'), ctx.texCoordMin(1), ctx.texCoordMin(2), 1 / (ctx.texResolution(1)/(ctx.texCoordMax(1) - ctx.texCoordMin(1))), 1 / (ctx.texResolution(2)/(ctx.texCoordMax(2) - ctx.texCoordMin(2))));
glUseProgram(0);
% Create gloperator from shader for later use by Screen('TransformTexture'):
ctx.textureExtractionOperator = CreateGLOperator(ctx.parentWin, [], ctx.textureExtractionShader, 'Extract Texture.');
% Ok, all PTB managed buffers and shaders loaded and set up.
% Lets create the VBO that we need to actually render anything in the
% end. VBO's are not supported yet by PTB's Screen, so we need to
% switch to our GL context for setup:
% DODO FIXME: Needed? Delete!
SwitchToGL(ctx.parentWin);
% Restore previous GL context binding:
RestoreGL;
% We're ready for the show!
contextcount = contextcount + 1;
% Init for this 'ctx' context done: Return it to usercode:
varargout{1} = ctx;
return;
end
% Destroy processing context -- Release all ressources and shaders:
if strcmpi(cmd, 'DestroyContext')
if nargin < 2
error('In "DestroyContext": You must provide the "context" to destroy!');
end
% Get context object:
ctx = varargin{1};
% Delete all offscreen windows, gloperators and buffers like IBO's
% VBO's, PBO's, FBO's etc...
deleteContextBuffers(ctx);
% Delete all shaders:
glDeleteProgram(ctx.textureExtractionShader);
glDeleteProgram(ctx.trackingRenderShader);
glDeleteProgram(ctx.silhouetteRenderShader);
RestoreGL;
% Shutdown done.
contextcount = max(contextcount - 1, 0);
% Return destroyed context:
ctx = [];
varargout{1} = ctx;
return;
end
% Set string to call via feval() to initiate a 3D render cycle for the 3D
% scene/object to be visualized:
if strcmpi(cmd, 'SetRenderCallback')
if nargin < 3
error('In "SetRenderCallback": You must provide the "context" and callback string!');
end
% Get context object:
ctx = varargin{1};
% Get the eval string:
renderCallback = varargin{2};
if ~ischar(renderCallback)
error('In "SetRenderCallback": Callback string must be a string, nothing else!');
end
% Assign:
ctx.renderCallback = renderCallback;
varargout{1} = ctx;
return;
end
% Update cycle, possibly followed by a render operation:
if strcmpi(cmd, 'Extract')
if nargin < 2
error(sprintf('In "%s": You must provide the "context"!', cmd)); %#ok<SPERR>
end
if nargin < 3
error(sprintf('In "%s": You must provide the "inputImage"!', cmd)); %#ok<SPERR>
end
if nargin >= 4
doNotRecycle = varargin{3};
else
doNotRecycle = [];
end
% Do recycle texture extraction buffer by default:
if isempty(doNotRecycle)
doNotRecycle = 0;
end
% Get context object:
ctx = varargin{1};
% Get texture handle of input video texture:
inputImage = varargin{2};
% Backup current OpenGL state:
BackupGL;
% Switch to OpenGL rendering context to be used for 3D scene rendering,
% and specifically for our silhouette render buffer:
SwitchToPTB;
Screen('BeginOpenGL', ctx.silhouetteBuffer);
% Backup 3D context state:
glPushAttrib(GL.ALL_ATTRIB_BITS);
% Perform 1st 3D render pass:
% Need zBuffer occlusion testing for silhouette rendering:
glEnable(GL.DEPTH_TEST);
% Need cleared buffer, including z buffer:
glClearColor(0,0,0,0);
glClear;
% Bind shader for silhouette rendering:
if debug~=-1
% We skip this if debug flag == -1 -- In that case the user wants
% to see the real rendered image instead of our silhouette
% encoding.
glUseProgram(ctx.silhouetteRenderShader);
end
% Set viewport and scissor to full target window area:
glViewport(0, 0, ctx.silhouetteWidth, ctx.silhouetteHeight);
glScissor(0, 0, ctx.silhouetteWidth, ctx.silhouetteHeight);
% Call the render callback function in workspace of our caller. We did
% not touch the modelview- or projection matrices, so the projections
% et al. should be ok...
evalin('caller', ctx.renderCallback);
% Don't need depth testing anymore:
glDisable(GL.DEPTH_TEST);
Screen('EndOpenGL', ctx.silhouetteBuffer);
% Silhouette should be ready in silhouette buffer...
if abs(debug) == 1
Screen('DrawTexture', ctx.parentWin, ctx.silhouetteBuffer, [], [], [], [], [], debugGain);
end
% Perform 2nd "pseudo 3D" render pass into trackingBuffer. This will
% again render the geometry, but with different encoding. A unwrapped
% texture map will be output, where each pixel corresponds to a surface
% point on the 3D object (aka texture coordinate). The color of each
% pixel encodes interpolated screen space (x,y,z) coordinates:
Screen('BeginOpenGL', ctx.trackingBuffer);
% No depth test here, as fragment depths doesn't encode anything
% meaningful during this pass:
glDisable(GL.DEPTH_TEST);
% Bind shader for tracking image rendering:
glUseProgram(ctx.trackingRenderShader);
% Set viewport and scissor to full trackbuffer window area:
glViewport(0, 0, ctx.texResolution(1), ctx.texResolution(2));
glScissor(0, 0, ctx.texResolution(1), ctx.texResolution(2));
% Call the render callback function in workspace of our caller. We did
% not touch the modelview- or projection matrices, so the projections
% et al. should be ok...
evalin('caller', ctx.renderCallback);
% Unbind all shaders:
glUseProgram(0);
% Just to make sure it's still off:
glDisable(GL.DEPTH_TEST);
% Restore 3D context state:
glPopAttrib;
% Trackingbuffer should be ready:
Screen('EndOpenGL', ctx.trackingBuffer);
if debug == 2
Screen('DrawTexture', ctx.parentWin, ctx.trackingBuffer, [], [], [], [], [], debugGain);
end
% We are in Screen()'s rendering context. Do the 2D image processing
% stuff:
% Need to attach the silhouette Buffers
glActiveTexture(GL.TEXTURE2);
glBindTexture(GL.TEXTURE_RECTANGLE_EXT, ctx.silhouetteTexture);
glActiveTexture(GL.TEXTURE0);
% Screen('TransformTexture') will apply the texture extraction operator
% to the inputImage video image texture and write the extracted texture
% into OutTextureBuffer. It will automatically bind inputImage and
% trackingBuffer as input textures to units 0 and 1, whereas we
% manually bound the silhouetteTexture to unit 2:
if doNotRecycle
recycleBuffer = [];
else
% Clear texture extraction result buffer:
Screen('FillRect', ctx.OutTextureBuffer, [0 0 0 0])
% Assign this buffer for recycling aka rewrite:
recycleBuffer = ctx.OutTextureBuffer;
end
extractedTexture = Screen('TransformTexture', ctx.trackingBuffer, ctx.textureExtractionOperator, inputImage, recycleBuffer);
if ~doNotRecycle
ctx.OutTextureBuffer = extractedTexture;
end
% Ok, the ctx.OutTextureBuffer should contain the extracted texture.
% Clear out all intermediate result buffers in preparation of next extraction cycle:
Screen('FillRect', ctx.trackingBuffer, [0 0 0 0])
Screen('FillRect', ctx.silhouetteBuffer, [0 0 0 0])
if debug == 3
Screen('DrawTexture', ctx.parentWin, extractedTexture, [], [], [], [], [], debugGain);
end
% Restore previous OpenGL context state:
RestoreGL;
% Ready: Return handle to our extracted texture buffer:
varargout{1} = extractedTexture;
[varargout{2}, varargout{3}] = Screen('GetOpenGLTexture', ctx.parentWin, extractedTexture);
return;
end
if strcmpi(cmd, 'DebugFlag')
if nargin < 2
error('Must provide new setting for debug flag!');
end
varargout{1} = debug;
varargout{2} = debugGain;
debug = varargin{1};
if nargin < 3
debugGain = [];
else
if length(varargin{2})~=4
error('In "DebugFlag": "debugGain" color modulation gain must be a 4 element [R,G,B,A] modulation color vector!');
end
debugGain = varargin{2};
end
return;
end
error(sprintf('Invalid subcommand ''%s'' specified!', cmd)); %#ok<SPERR>
return;
% Internal helper functions:
function SwitchToGL(win)
% Switch to our OpenGL context, but keep a backup of original
% drawstate. We do lazy switching if possible:
[currentwin, IsOpenGLRendering] = Screen('GetOpenGLDrawMode');
if ~IsOpenGLRendering
% PTB's context active: Switch to OpenGL rendering for our parent window:
Screen('BeginOpenGL', win);
else
% In rendering context. Is it ours? If yes, then there isn't anything
% to do...
if currentwin ~= win
% No, a different windows context is active: First switch to PTB
% mode, then switch to ours:
% Switch to our parentWin's PTB context:
Screen('EndOpenGL', currentwin);
% Switch to our parentWin's GL context:
Screen('BeginOpenGL', win);
end
end
return;
function SwitchToPTB
% Switch from our OpenGL context, but keep a backup of original
% drawstate. We do lazy switching if possible:
[currentwin, IsOpenGLRendering] = Screen('GetOpenGLDrawMode');
if ~IsOpenGLRendering
% PTB's context is already active: Nothing to do.
else
% In rendering context. Switch back to PTB - and to our parentWin:
Screen('EndOpenGL', currentwin);
end
return;
function BackupGL
global moglExtractTexture_OriginalContext;
if ~isempty(moglExtractTexture_OriginalContext)
error('BackupGL called twice in succession without intermediate RestoreGL! Ordering inconsistency!');
end
[currentwin, IsOpenGLRendering] = Screen('GetOpenGLDrawMode');
if IsOpenGLRendering
moglExtractTexture_OriginalContext = currentwin;
end
return;
function RestoreGL
global moglExtractTexture_OriginalContext;
[currentwin, IsOpenGLRendering] = Screen('GetOpenGLDrawMode');
if isempty(moglExtractTexture_OriginalContext)
% PTB was in Screen drawing mode: Switch to that mode, if not active:
if IsOpenGLRendering
Screen('EndOpenGL', currentwin);
end
return;
end
% Need to restore to GL context if not already active:
if ~IsOpenGLRendering
Screen('BeginOpenGL', moglExtractTexture_OriginalContext);
else
% OpenGL context active. Ours? If so -> Nothing to do.
if currentwin ~= moglExtractTexture_OriginalContext
% Nope. Need to switch:
Screen('EndOpenGL', currentwin);
Screen('BeginOpenGL', moglExtractTexture_OriginalContext);
end
end
% Restore to default:
moglExtractTexture_OriginalContext = [];
return;
function deleteContextBuffers(ctx)
BackupGL;
SwitchToPTB;
% Close all offscreen windows and their associated textures:
Screen('Close', [ctx.OutTextureBuffer, ctx.trackingBuffer, ctx.silhouetteBuffer]);
% Close our operators:
Screen('Close', ctx.textureExtractionOperator);
return;
|