This file is indexed.

/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;