Handmade Hero»Forums»Code
90 posts
Sort drawing by swapping indices in openGL indexbuffer
Edited by C_Worm on Reason: Initial post
Hello, as the title says im trying to sort my game entites by swapping the indices in my index buffer or ( GL_ELEMENT_ARRAY_BUFFER) in OpenGL.

So all my entities have a ibo_id when they're push'd or created during game initialization.

The ibo_id is the first of 6 indices in the ibo which, as far as i've understood the indexbuffer decides which quad will be drawn first.

so these are the indices of the first quad (triangle-pair)
ibo[0] = 0;
ibo[1] = 1;
ibo[2] = 2;
ibo[3] = 0;
ibo[4] = 2;
ibo[5] = 3;

and so on....

what my swap function does is:

1. check if entity_1.pos.y is less then entity_2.pos.y
2. If it is, check if ibo[entity_1.ibo_id] is less than ibo[entity_2.ibo_id].
3 If it is, swap the 6 values corresponding to the entities ibo_id's

The thing is that if I only use the function on 2 entites like SwapOrder(game->player, game->monster);
the player is draw ontop of the monster when it goes below the monster and vice verse.

While if I first check the player against all tiles and then against the monster, the player is not drawn ontop of the monster...

I've checked the values of the index buffer and they seem fine meaning that the player's ibo_values are greater than the monsters
in both cases.

Im starting to wonder if the indexbuffer is not doing what i assume it does, that is, draw the last indices ontop of the first indices??


Code-snippets attached:
1. PushEntity()
2. SwapOrderInIBO()
3. Full main.cpp using SwapOrderInIBO() on lines ( 549 - 667 )

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ENTITY *PushEntity(GAME_HANDLER *game, u32 size)
{
	assert(game->entity_used + size <= game->entity_batch_count);
    
	ENTITY *entity 		= 0;
	CJ_VTX_QUAD *vtx	= 0;
    
	entity			= game->entity_batch + game->entity_used;;
	entity->id 		= game->entity_id;
	entity->ibo_id		= game->ibo_id;
    
	vtx			= game->pVertex + game->vtx_id;
    
    
	// vtx_id is for pushing 4 * CJ_VTX_QUAD
	game->vtx_id		+= 4 * size;
	game->ibo_id		+= 6 * size;
	game->entity_used += size;
	game->entity_id++;
	
    
	return entity;
}



 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
void SwapOrderInIBO(CJ_RENDERER *renderer, ENTITY *e1, ENTITY *e2)
{
	if(e1->pos.y < e2->pos.y)
	{
		u32 e1_ibo_index 	= e1->ibo_id;
		u32 e2_ibo_index 	= e2->ibo_id;


		u32 e1_ibo_value 	= renderer->ibo_sprite.base[e1_ibo_index];
		u32 e2_ibo_value 	= renderer->ibo_sprite.base[e2_ibo_index];
		

		if(e1_ibo_value < e2_ibo_value)
		{
			for(u32 i = 0; i < 6; i++)
			{

				e1_ibo_value = renderer->ibo_sprite.base[e1_ibo_index + i];
				e2_ibo_value = renderer->ibo_sprite.base[e2_ibo_index + i];

				u32 temp_ibo_value = e1_ibo_value;
				renderer->ibo_sprite.base[e1_ibo_index + i] = e2_ibo_value;
				renderer->ibo_sprite.base[e2_ibo_index + i] = temp_ibo_value;
			}
		}

	}
	else if(e1->pos.y > e2->pos.y)
	{
		u32 e1_ibo_index 	= e1->ibo_id;
		u32 e2_ibo_index 	= e2->ibo_id;

		u32 e1_ibo_value 	= renderer->ibo_sprite.base[e1_ibo_index];
		u32 e2_ibo_value 	= renderer->ibo_sprite.base[e2_ibo_index];

		if(e1_ibo_value > e2_ibo_value)
		{
			for(u32 i = 0; i < 6; i++)
			{

				e1_ibo_value = renderer->ibo_sprite.base[e1_ibo_index + i];
				e2_ibo_value = renderer->ibo_sprite.base[e2_ibo_index + i];

				u32 temp_ibo_value = e1_ibo_value;
				renderer->ibo_sprite.base[e1_ibo_index + i] = e2_ibo_value;
				renderer->ibo_sprite.base[e2_ibo_index + i] = temp_ibo_value;
			}
		}
	}
}


  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
bool running = true;

#include "assert.h"
#include "cj_engine.h"

GLenum err = 0;




struct TEXT_INFO
{
	V2f sentence_pos;
	V2f sentence_size;
	u32 n_files_in_dir;
};

struct TILEMAP
{
	u32 w;
	u32 h;
	ENTITY *entity;
};

struct GAME_HANDLER
{
	ENTITY *entity_batch;
	u32 entity_batch_count;
	u32 entity_used;
	u32 entity_id;
    
	CJ_VTX_QUAD *pVertex;
	u32 vtx_id;
	u32 ibo_id;
    
	TILEMAP tilemap;
    
	ENTITY *player;
	ENTITY *monster;
	ENTITY *current_chosen_atlas;
    
	ENTITY *camera;
    
    
};



bool CursorInsideText(CJ_PLATFORM platform, TEXT_INFO ti);
TEXT_INFO DrawText(CJ_PLATFORM *platform, CJ_RENDERER *renderer, u32 texture_index, char *str, V2f pos, V4f col);
void SwapOrderInIBO(CJ_RENDERER *renderer, ENTITY *e1, ENTITY *e2);

void GetDirContent(TEXT_INFO *ti, char *storage, char *dir, u32 max_filesz_name)
{
	ti->n_files_in_dir = 0;
	WIN32_FIND_DATA find_data 		= {};
	LARGE_INTEGER file_sz			= {};
	HANDLE hFind				= {};
    
	strcpy(storage, dir);
	strcat(storage, "/*");
	ti->n_files_in_dir++;
    
	hFind = FindFirstFile(storage, &find_data);
    
	if(hFind == INVALID_HANDLE_VALUE) 
		printf("couldn't find file in %s\n", storage);
	else
		storage += max_filesz_name;
    
	while(FindNextFile(hFind, &find_data) != 0)
	{
		if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			strcpy(storage, find_data.cFileName);
			storage += max_filesz_name;
			ti->n_files_in_dir++;
            
		}
		else
		{
			char *dir_assets = "assets/";
			strcpy(storage, dir_assets);
			strcat(storage, find_data.cFileName);
			storage += max_filesz_name;
			ti->n_files_in_dir++;
		}
	}
    
}



ENTITY *PushEntity(GAME_HANDLER *game, u32 size)
{
	assert(game->entity_used + size <= game->entity_batch_count);
    
	ENTITY *entity 		= 0;
	CJ_VTX_QUAD *vtx	= 0;
    
	entity			= game->entity_batch + game->entity_used;;
	entity->id 		= game->entity_id;
	entity->ibo_id		= game->ibo_id;
    
	vtx			= game->pVertex + game->vtx_id;
    
    
	// vtx_id is for pushing 4 * CJ_VTX_QUAD
	game->vtx_id		+= 4 * size;
	game->ibo_id		+= 6 * size;
	game->entity_used += size;
	game->entity_id++;
	
    
	return entity;
}

void UpdateVBO_Entity(CJ_VBO *vbo, GAME_HANDLER *game)
{
	CJ_VTX_QUAD *pVertex = (CJ_VTX_QUAD*)vbo->base;
    
	assert(game->entity_used <= game->entity_batch_count);
    
	for(u32 i = 0; i < game->entity_used; i++)
	{
		V2f pos 	= game->entity_batch[i].pos;
		V2f size 	= game->entity_batch[i].size;
		V4f col 	= game->entity_batch[i].col;
		V2f tCoord_p	= game->entity_batch[i].texcoord_pos;
		V2f tCoord_sz	= game->entity_batch[i].texcoord_size;
        
		pVertex[(i * 4) + 0].pos = v2f(pos.x, pos.y);
		pVertex[(i * 4) + 1].pos = v2f(pos.x, pos.y + size.y);
		pVertex[(i * 4) + 2].pos = v2f(pos.x + size.x, pos.y + size.y);
		pVertex[(i * 4) + 3].pos = v2f(pos.x + size.x, pos.y);
        
		pVertex[(i * 4) + 0].col = col;
		pVertex[(i * 4) + 1].col = col;
		pVertex[(i * 4) + 2].col = col;
		pVertex[(i * 4) + 3].col = col;
        
		pVertex[(i * 4) + 0].tCoord = v2f(tCoord_p.x, tCoord_p.y);
		pVertex[(i * 4) + 1].tCoord = v2f(tCoord_p.x, tCoord_p.y + tCoord_sz.y);
		pVertex[(i * 4) + 2].tCoord = v2f(tCoord_p.x + tCoord_sz.x, tCoord_p.y + tCoord_sz.y);
		pVertex[(i * 4) + 3].tCoord = v2f(tCoord_p.x + tCoord_sz.y, tCoord_p.y);
        
	}
    
}


bool CursorInsideText(CJ_PLATFORM platform, TEXT_INFO ti)
{
	if(platform.cursor_pos.x > ti.sentence_pos.x &&
	   platform.cursor_pos.x < ti.sentence_size.x &&
	   platform.cursor_pos.y > ti.sentence_pos.y &&
	   platform.cursor_pos.y < ti.sentence_size.y)
		return true;
	else
		return false;
}

TEXT_INFO DrawText(CJ_PLATFORM *platform, CJ_RENDERER *renderer, u32 texture_index, char *str, V2f pos, V4f col)
{
	TEXT_INFO ti = {};
    
	char line[1024] = {};
	strcpy(line, str);
	
	// xp & yp is baseline in stbtt_GetPackedQuad();
	float yp = 0.0f;
	float xp = 0.0f;
	
	float t_pos_x = 0.0f;
	float t_pos_y = 0.0f;
	float t_sz_x = 0.0f;
	float t_sz_y = 0.0f;
	
	float c_x = 0.0f;
	float c_y = 0.0f;
	
	float c_w = 0.0f;
	float c_h = 0.0f;
	
	float bigges_h = 0.0f;
    
	u32 n_quads_written = 0;
    
	glBindVertexArray(renderer->VAO_font);
	UseShaderProgram(&renderer->shader[2]);
	glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo_text.id);
	glBindTexture(GL_TEXTURE_2D, renderer->texture_batch[texture_index].texture_id);
    
	CJ_VTX_QUAD *advPointer = (CJ_VTX_QUAD*)renderer->vbo_text.base;
	TEXTUREINFO *tex_i = &renderer->texture_batch[texture_index];
	
	for(i32 i = 0; i < strlen(line); i++)
	{
	    stbtt_GetPackedQuad(tex_i->packed_char, 1024, 1024, line[i] - ' ', &xp, &yp, &tex_i->glyph_quad, 0);
	    
	    t_pos_x = tex_i->glyph_quad.s0;
	    t_pos_y = 1.0f - tex_i->glyph_quad.t1;
	    t_sz_x = tex_i->glyph_quad.s1 - tex_i->glyph_quad.s0;
	    t_sz_y = (1.0f - tex_i->glyph_quad.t0) - (1.0f - tex_i->glyph_quad.t1);
        
	    c_x = pos.x + (tex_i->glyph_quad.x0)  / 1080.0f;
	    c_y = pos.y + ((yp + (yp - tex_i->glyph_quad.y1))) / 960.0f;
	    
	    c_w = (tex_i->glyph_quad.x1 - tex_i->glyph_quad.x0)  / 1080.0f;
	    c_h = (tex_i->glyph_quad.y1 - tex_i->glyph_quad.y0)  / 960.0f;
        
	    CJ_VTX_QUAD vertices[6] = {};
	    vertices[0].pos 	= v2f(c_x, c_y);
	    vertices[0].col 	= v4f(col.x, col.y, col.z, col.w);
	    vertices[0].tCoord 	= v2f(t_pos_x, t_pos_y);
        
	    vertices[1].pos 	= v2f(c_x, c_y + c_h);
	    vertices[1].col 	= v4f(col.x, col.y, col.z, col.w);
	    vertices[1].tCoord 	= v2f(t_pos_x, t_pos_y + t_sz_y);
        
	    vertices[2].pos 	= v2f(c_x + c_w, c_y + c_h);
	    vertices[2].col 	= v4f(col.x, col.y, col.z, col.w);
	    vertices[2].tCoord 	= v2f(t_pos_x + t_sz_x, t_pos_y + t_sz_y);
        
	    vertices[3].pos 	= v2f(c_x, c_y);
	    vertices[3].col 	= v4f(col.x, col.y, col.z, col.w);
	    vertices[3].tCoord 	= v2f(t_pos_x, t_pos_y);
        
	    vertices[4].pos 	= v2f(c_x + c_w, c_y + c_h);
	    vertices[4].col 	= v4f(col.x, col.y, col.z, col.w);
	    vertices[4].tCoord 	= v2f(t_pos_x + t_sz_x, t_pos_y + t_sz_y);
        
	    vertices[5].pos 	= v2f(c_x + c_w, c_y);
	    vertices[5].col 	= v4f(col.x, col.y, col.z, col.w);
	    vertices[5].tCoord 	= v2f(t_pos_x + t_sz_x, t_pos_y);
        
	    memcpy(advPointer, vertices, sizeof(vertices));
	    // offset means offest into the VBO ont the GPU (vbo_text) not the buffer on the CPU (advPointer)
	    glBufferSubData(GL_ARRAY_BUFFER, 0, renderer->vbo_text.size, (void*)renderer->vbo_text.base);
        
	    advPointer += 6;
	    n_quads_written++;
        
	    if(i == 0)
		    ti.sentence_pos = v2f(c_x, c_y);
        
	    if(c_y + c_h > bigges_h)
		    bigges_h = c_y + c_h;
	}
    
	// TODO: Could check for the largest size instead of the size of the last characterbox.
	ti.sentence_size = v2f(c_x + c_w, bigges_h);
	glDrawArrays(GL_TRIANGLES, 0, n_quads_written * 6);
    
	return ti;
} 

void OnGameInit(CJ_PLATFORM *platform, CJ_RENDERER *renderer, GAME_HANDLER *game)
{
	static bool is_init = false;
	if(!is_init)
	{
        
		game->entity_batch 		= (ENTITY*)calloc(1000, sizeof(ENTITY));
		game->entity_batch_count	= 1000 * sizeof(ENTITY);
		game->pVertex 			= (CJ_VTX_QUAD*)renderer->vbo_sprite.base;

		V2f tp = v2f((32.0f / 512.0f), (32.0f / 512.0f));
		game->player 			= PushEntity(game, 1);
		game->player->pos 		= v2f(1.0f, 1.7f); 
		game->player->size 		= v2f(0.03f, 0.1f); 
		game->player->col 		= v4f(1.0f, 1.0f, 1.0f, 1.0f); 
		game->player->texcoord_pos 	= v2f(0.0f, 0.0f);
		game->player->texcoord_size 	= v2f(1.0f, 1.0f);
        
		game->monster 			= PushEntity(game, 1);
		game->monster->pos 		= v2f(1.0f, 1.0f); 
		game->monster->size 		= v2f(0.2f, 0.2f); 
		game->monster->col 		= v4f(1.0f, 1.0f, 1.0f, 0.8f); 
		game->monster->texcoord_pos 	= v2f(3.0f * tp.x, 1.0f - (1.0f * tp.y));
		game->monster->texcoord_size 	= v2f(1.0f * tp.x, 1.0f * tp.y);

        
		u32 tilemap_w = 10;
		u32 tilemap_h = 10;
		V2f start_pos = v2f(0.5f, 0.5f);
		V2f tile_size = v2f(0.11f, 0.11f);
		game->tilemap.entity = PushEntity(game, tilemap_w * tilemap_h);
		game->tilemap.w = tilemap_w;
		game->tilemap.h = tilemap_h;

        
        
		for(u32 y = 0; y < tilemap_h; y++)
		{
			for(u32 x = 0; x < tilemap_w; x++)
			{
				u32 index = x + (y * tilemap_w);
                
				float randW = CJ_RandFRange(0.011f, 0.074f, CJ_RAND_DONT_INCLUDE);
				float randH = CJ_RandFRange(0.011f, 0.074f, CJ_RAND_DONT_INCLUDE);
				float separation = 0.002f;
				printf("randW: %f\n", randW);
				printf("randH: %f\n", randH);
                
				V2f pos = v2f(start_pos.x + (x * tile_size.x), start_pos.y + (y * tile_size.y));
				V2f size = v2f(tile_size.x - separation, tile_size.y - separation);
				V4f col = v4f(1.0f, 1.0f, 1.0f, 1.0f);
                
				game->tilemap.entity[index].pos = pos;
				game->tilemap.entity[index].size = size;
				game->tilemap.entity[index].col = col;
                
				game->tilemap.entity[index].texcoord_pos 	= v2f(0.0f * tp.x, 1.0f - (2.0f * tp.y));
				game->tilemap.entity[index].texcoord_size 	= v2f(1.0f * tp.x, 1.0f * tp.y);

				game->tilemap.entity[index].ibo_id = game->tilemap.entity[0].ibo_id + (6 * index);
                
                
			}
            
		}
        
        
        
		game->camera = PushEntity(game, 1);
		game->camera->pos = v2f(0.0f, 0.0f);

		game->current_chosen_atlas 			= PushEntity(game, 1);
		game->current_chosen_atlas->pos 		= v2f(0.0f, 1.2f); 
		game->current_chosen_atlas->size 		= v2f(0.6f, 0.8f); 
		game->current_chosen_atlas->col 		= v4f(1.0f, 1.0f, 1.0f, 1.0f); 
		game->current_chosen_atlas->texcoord_pos 	= v2f(0.0f, 0.0f);
		game->current_chosen_atlas->texcoord_size 	= v2f(1.0f, 1.0f);
        
        
        
		is_init = true;
	}
    
}

void OnGameUpdate(CJ_PLATFORM *platform, CJ_RENDERER *renderer, GAME_HANDLER *game)
{
    
	if(GetKey(platform, GLFW_KEY_ESCAPE))
	{
		running = false;
	}
    
	float mov = 0.003f;
    
	game->camera->vel = v2f(0.f, 0.f);
	if(GetKey(platform, GLFW_KEY_UP))
	{
		game->camera->vel.y = mov;
	}
	if(GetKey(platform, GLFW_KEY_DOWN))
	{
		game->camera->vel.y = -mov;
	}
	if(GetKey(platform, GLFW_KEY_RIGHT))
	{
		game->camera->vel.x = mov;
	}
	if(GetKey(platform, GLFW_KEY_LEFT))
	{
		game->camera->vel.x = -mov;
	}
    
    
	game->player->pos += game->camera->vel;
    
	bool clamped = false;
	TILEMAP *tilemap = &game->tilemap;
	float clamp_left 	= tilemap->entity[0].pos.x + tilemap->entity[0].size.x;
	float clamp_right 	= tilemap->entity[tilemap->w - 1].pos.x;
	float clamp_bottom 	= tilemap->entity[0].pos.y + tilemap->entity[0].size.y;
	float clamp_top 	= tilemap->entity[tilemap->w * tilemap->h - 1].pos.y;
    
	if(game->player->pos.x <= clamp_left)
	{
		game->player->pos.x = clamp_left;
		game->player->pos -= game->camera->vel;
		clamped = true;
	}
	if(game->player->pos.x + game->player->size.x >= clamp_right)
	{
		game->player->pos.x = clamp_right - game->player->size.x;
		game->player->pos -= game->camera->vel;
		clamped = true;
        
	}
	if(game->player->pos.y < clamp_bottom)
	{
		game->player->pos.y = clamp_bottom;
		game->player->pos -= game->camera->vel;
		clamped = true;
	}
    
	if(game->player->pos.y > clamp_top)
	{
		game->player->pos.y = clamp_top;
		game->player->pos -= game->camera->vel;
        
		clamped = true;
	}
    
	ENTITY *monster = game->monster;
	ENTITY *player = game->player;
    
	if(monster->pos.x != player->pos.x || monster->pos.y != player->pos.y)
	{
		V2f direction = {};
		direction.x = player->pos.x - monster->pos.x;
		direction.y = player->pos.y - monster->pos.y;
		direction = Unit_v2f(direction);
        
		monster->pos.x += 0.0001f * direction.x;
		monster->pos.y += 0.0001f * direction.y;
	}

	if(clamped)
	{
		u32 start_entity_id = game->tilemap.entity->id;
		u32 n_tiles = (game->tilemap.w  * game->tilemap.h);
		for(u32 i = start_entity_id; i < start_entity_id + n_tiles; i++)
		{
			game->entity_batch[i].pos -= game->camera->vel;
		}
	}
    
	char buffer[256] = {};
	sprintf(buffer, "game.player->pos: (%f / %f)", game->player->pos.x, game->player->pos.y);
    
	static V4f tCol 	= COLOR_ORANGE;
	static V4f tCol_save 	= COLOR_GREEN;
	static V4f tCol_select 	= COLOR_GREEN;
	// can create ti before gameloop to prevent setting the color after the text is rendered.
	TEXT_INFO ti = {};
	ti = DrawText(platform, renderer, 1, buffer, v2f(0.2f, 1.7f), tCol);
	if(CursorInsideText(*platform, ti)) 	{ tCol = COLOR_RED; } else { tCol = COLOR_ORANGE; }
    
	sprintf(buffer, "time since start: (%f)", GetTime());
	ti = DrawText(platform, renderer, 2, buffer, v2f(0.0f, 1.8f), COLOR_GREEN);
	
	sprintf(buffer, "platform.cursor_pos: (%f / %f)", platform->cursor_pos.x, platform->cursor_pos.y);
	DrawText(platform, renderer, 1, buffer, v2f(0.4f, 0.3f), v4f(1.0f, 1.0f, 1.0f, 1.0f));
    
	ti = DrawText(platform, renderer, 2, "Select atlas", v2f(1.7f, 1.3f), tCol_select);
	if(CursorInsideText(*platform, ti)) 	{ tCol_select = COLOR_RED; } else { tCol_select = COLOR_GREEN; }
    
    
	ti = DrawText(platform, renderer, 2, "Save map", v2f(1.7f, 1.2f), tCol_save);
	if(CursorInsideText(*platform, ti)) 	
	{ 
		tCol_save = COLOR_RED; 
		if(GetMouseOnce(GLFW_MOUSE_BUTTON_LEFT))
		{
			ti = DrawText(platform, renderer, 2, "LEFT MOUSE PRESSED", v2f(0.7f, 1.3f), tCol_select);
		}
	} else 
	{ 
		tCol_save = COLOR_GREEN; 
	}
    
    
    
	char dir_content[24][1024] = {};
	GetDirContent(&ti, dir_content[0], "assets", 1024);
	u32 files_in_dir = ti.n_files_in_dir;
	static bool init = false;
	if(!init)
	{
		init = true;
		for(u32 i = 0; i < ti.n_files_in_dir; i++)
		{
			printf("dir_content[%d]: %s\n", i, dir_content[i]);
		}
	}
    
	static V4f cont_col[24] = {};
	static V4f yes_no[2] = {};
	static bool bLoad_texture = false;
	
	static char file_to_choose[256] = {};
	for(u32 i = 0; i < files_in_dir; i++)
	{
		ti = DrawText(platform, renderer, 2, dir_content[i], v2f(1.6f, 1.8f - i * 0.1f), cont_col[i]);
		if(CursorInsideText(*platform, ti)) 	
		{ 
			cont_col[i] = COLOR_RED; 
			if(GetMouseOnce(GLFW_MOUSE_BUTTON_LEFT) && !bLoad_texture)
			{
                strcpy(file_to_choose, dir_content[i]);
                bLoad_texture = true;
			}
            
		} 
		else 
		{ 
			cont_col[i] = COLOR_GREEN; 
		}
        
		if(bLoad_texture)
		{
			ti = DrawText(platform, renderer, 2, "Load texture?", v2f(0.7f, 1.0f), COLOR_GREEN);
			static V4f yes_col = {};
			ti = DrawText(platform, renderer, 2, "Yes", v2f(0.7f, 0.9f), yes_col);
			if(CursorInsideText(*platform, ti)) 	
			{ 
				yes_col = COLOR_RED; 
				if(GetMouseOnce(GLFW_MOUSE_BUTTON_LEFT))
				{
					LoadTexture(&renderer->texture_batch[0], file_to_choose, LOAD_IMAGE, 0);
					bLoad_texture = false;
				}
                
			} 
			else if(GetKeyOnce(GLFW_KEY_ENTER))
			{
				LoadTexture(&renderer->texture_batch[0], file_to_choose, LOAD_IMAGE, 0);
				bLoad_texture = false;
			}
			else
			{ 
				yes_col = COLOR_GREEN; 
			}
            
			static V4f no_col = {};
			ti = DrawText(platform, renderer, 2, "No", v2f(0.8f, 0.9f), no_col);
			if(CursorInsideText(*platform, ti)) 	
			{ 
				no_col = COLOR_RED; 
				if(GetMouseOnce(GLFW_MOUSE_BUTTON_LEFT))
				{
					bLoad_texture = false;
				}
                
			} 
			else 
			{ 
				no_col = COLOR_GREEN; 
			}
		}
        
	}

	u32 start_entity_id = game->tilemap.entity->id;
	u32 n_tiles = (game->tilemap.w  * game->tilemap.h);
	for(u32 i = 0; i < 60; i++)
	{
		SwapOrderInIBO(renderer, game->player, &game->entity_batch[i]);
	}

	for(u32 i = 0; i < n_tiles; i++)
	{
		SwapOrderInIBO(renderer, game->monster, &game->tilemap.entity[i]);
	}

	//if(game->player->pos.y < game->monster->pos.y)
	//{
	//	if(renderer->ibo_sprite.base[game->player->ibo_id] < renderer->ibo_sprite.base[game->monster->ibo_id])
	//	{
	//	           SwapOrderInIBO(renderer, game->player, game->monster);
	//	}
	//}
        SwapOrderInIBO(renderer, game->player, game->monster);
	char iboValue_player[32] = {};
	sprintf(iboValue_player, "player ibo value: %d", renderer->ibo_sprite.base[game->player->ibo_id]);
	char iboValue_mon[32] = {};
	sprintf(iboValue_mon, "monster ibo value: %d", renderer->ibo_sprite.base[game->monster->ibo_id]);
	DrawText(platform, renderer, 2, iboValue_player, v2f(0.3f, 0.2f), COLOR_GREEN);
	DrawText(platform, renderer, 2, iboValue_mon, v2f(0.3f, 0.1f), COLOR_GREEN);

    
}


void SwapOrderInIBO(CJ_RENDERER *renderer, ENTITY *e1, ENTITY *e2)
{
	if(e1->pos.y < e2->pos.y)
	{
		u32 e1_ibo_index 	= e1->ibo_id;
		u32 e2_ibo_index 	= e2->ibo_id;


		u32 e1_ibo_value 	= renderer->ibo_sprite.base[e1_ibo_index];
		u32 e2_ibo_value 	= renderer->ibo_sprite.base[e2_ibo_index];
		

		if(e1_ibo_value < e2_ibo_value)
		{
			for(u32 i = 0; i < 6; i++)
			{

				e1_ibo_value = renderer->ibo_sprite.base[e1_ibo_index + i];
				e2_ibo_value = renderer->ibo_sprite.base[e2_ibo_index + i];

				u32 temp_ibo_value = e1_ibo_value;
				renderer->ibo_sprite.base[e1_ibo_index + i] = e2_ibo_value;
				renderer->ibo_sprite.base[e2_ibo_index + i] = temp_ibo_value;
			}
		}

	}
	else if(e1->pos.y > e2->pos.y)
	{
		u32 e1_ibo_index 	= e1->ibo_id;
		u32 e2_ibo_index 	= e2->ibo_id;

		u32 e1_ibo_value 	= renderer->ibo_sprite.base[e1_ibo_index];
		u32 e2_ibo_value 	= renderer->ibo_sprite.base[e2_ibo_index];

		if(e1_ibo_value > e2_ibo_value)
		{
			for(u32 i = 0; i < 6; i++)
			{

				e1_ibo_value = renderer->ibo_sprite.base[e1_ibo_index + i];
				e2_ibo_value = renderer->ibo_sprite.base[e2_ibo_index + i];

				u32 temp_ibo_value = e1_ibo_value;
				renderer->ibo_sprite.base[e1_ibo_index + i] = e2_ibo_value;
				renderer->ibo_sprite.base[e2_ibo_index + i] = temp_ibo_value;
			}
		}
	}
}






int main()
{
    
	CJ_PLATFORM platform 	= CreatePlatform(640, 480, "Platform", 0);
	CJ_RENDERER renderer 	= CreateRenderer();
	CJ_InitRandom();
	GAME_HANDLER game 	= {};
    
	while(!glfwWindowShouldClose(platform.window) && running)
	{
        
		UpdatePlatform(&platform);

		glViewport(0, 0, platform.win_w, platform.win_h);
		RenderClear(v4f(0.1f, 0.1f, 0.1f, 1.0f));
        
		OnGameInit(&platform, &renderer, &game);
		OnGameUpdate(&platform, &renderer, &game);
        
        
		glBindVertexArray(renderer.VAO_texture_quad);
		glBindBuffer(GL_ARRAY_BUFFER, renderer.vbo_sprite.id);
        
		UpdateVBO_Entity(&renderer.vbo_sprite, &game);
		glBufferSubData(GL_ARRAY_BUFFER, 0, renderer.vbo_sprite.size, (void*)renderer.vbo_sprite.base);
		u32 size_bytes = renderer.ibo_sprite.size / sizeof(u32);
		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, renderer.ibo_sprite.size / sizeof(u32), (void*)renderer.ibo_sprite.base);
        
		glBindTexture(GL_TEXTURE_2D, renderer.texture_batch[0].texture_id);
		DrawElements(&renderer, 1, 0, game.entity_batch_count, 0);
        
        
        
		SwapBuffers(platform);
	}
    
    
    
	glfwTerminate();
    
    
    return 0;
}
Mārtiņš Možeiko
2201 posts / 1 project
Sort drawing by swapping indices in openGL indexbuffer
Edited by Mārtiņš Možeiko on
OpenGL renders primitives in order index buffer specifies them. Whoever comes first gets rasterized first, before GL goes to rasterize next primitive.

I don't know your shaders and GL configuration - but are you using depth buffer? If you are you must make sure depth values are correct so they don't cancel other primitives out. Or simply disable depth test with glDisable.
90 posts
Sort drawing by swapping indices in openGL indexbuffer
Im not using depth buffer, here are the shaders, very primitive.... and here's my renderer which sets up the opengl configuration.

!!!!! side note !!!!

In my main() im using glBufferSubdata() every frame on the index buffer and when i supply the full buffersize in bytes to the "GLsizeiptr size" param, the buffer which was initialized in CreateRenderer(), the quads get really messed up, which was a supprise to me but it works okay if i give it full buffersize / sizeof(u32). wth?!

!!!! end of side note !!!!



Vertex shader
 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
#version 330 core


layout (location = 0) in vec2 in_Pos;
layout (location = 1) in vec4 in_Col;
layout (location = 2) in vec2 in_tCoord;


out vec4 VS_out_col;
out vec2 VS_out_tCoord;


void main()
{

	vec4 v4_pos;
	v4_pos.x = in_Pos.x - 1.0f;
	v4_pos.y = in_Pos.y - 1.0f;
	v4_pos.z = 0.0f;
	v4_pos.w = 1.0f;

	gl_Position = v4_pos;
	VS_out_col = in_Col;
	VS_out_tCoord = in_tCoord;


}




Frag shader
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#version 330 core


in vec4 VS_out_col;
in vec2 VS_out_tCoord;


out vec4 textured_surface;

uniform sampler2D in_texture;

void main()
{
	vec4 col = VS_out_col;
	textured_surface = texture(in_texture, VS_out_tCoord) * col;
}




Renderer
  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
#ifndef CJ_RENDER_H
#define CJ_RENDER_H

#define LOAD_IMAGE 0
#define LOAD_FONT 1

#define QUAD_IMAGE 0
#define QUAD_TEXT_STATIC 1
#define QUAD_TEXT_DYNAMIC 2
#define QUAD_NOT_INITIALIZED 0
#define QUAD_INITIALIZED 1

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"


#include "cj_shader.h"

struct TEXTUREINFO
{
    
    	u32 texture_id;
    	
    	// for loading font textures
    	stbtt_packedchar packed_char[126];
    	stbtt_pack_context pack_context;
    	stbtt_aligned_quad glyph_quad;
    	
    	float xp;
    	float yp;
    	
    	u32 atlas_w = 1024;
    	u32 atlas_h = 1024;
    	
    	unsigned char *font_file_data;
    	unsigned char *pixels;

	u32 texture_loaded;
};

struct CJ_VTX_QUAD
{
	V2f pos;
	V4f col;
	V2f tCoord;
};

struct CJ_VTX_ATTRIB
{
	u32 size;
	GLenum type;
	u32 stride;
	u32 offset;
};

struct CJ_VBO
{
	u32 id;
	u8 *base;
	u32 used;
	u32 size;
};

struct CJ_IBO
{
	u32 id;
	u32 *base;
	u32 used;
	u32 size;
};

struct CJ_RENDERER
{
	u32 VAO_texture_quad;
	u32 VAO_font;

	CJ_VBO vbo_sprite;
	CJ_VBO vbo_text;

	CJ_IBO ibo_sprite;

	SHADER_PROGRAM shader[10];

	TEXTUREINFO texture_batch[10];


};

void InitializeVBO(CJ_VBO *vbo, u8* base, u32 size, u32 n_attribs, CJ_VTX_ATTRIB *vtx_attrib);
CJ_RENDERER CreateRenderer();
void InvertSTBBuffer(unsigned char *memory, u32 w, u32 h);
void LoadTexture(TEXTUREINFO *tex_i, char *texture_path, u32 texture_type, i32 glyph_size);

void RenderClear(V4f col);
void DrawElements(CJ_RENDERER *renderer, u32 shader_index, u32 tex_index, u32 n_ents_to_draw, u32 n_ents_offset);

void InitializeVBO(CJ_VBO *vbo, u8* base, u32 size, u32 n_attribs, CJ_VTX_ATTRIB *vtx_attrib)
{
	vbo->base = base;
	vbo->size = size;

	glGenBuffers(1, &vbo->id);
	glBindBuffer(GL_ARRAY_BUFFER, vbo->id);
	glBufferData(GL_ARRAY_BUFFER, vbo->size, vbo->base, GL_DYNAMIC_DRAW);

	for(u32 i = 0; i < n_attribs; i++)
	{
		vtx_attrib[i].offset *= sizeof(float);
		vtx_attrib[i].stride *= sizeof(float);
		glEnableVertexAttribArray(i);	glVertexAttribPointer(i, vtx_attrib[i].size, vtx_attrib[i].type, false, vtx_attrib[i].stride, (void*)vtx_attrib[i].offset);
	}


}

CJ_RENDERER CreateRenderer()
{

	CJ_RENDERER renderer = {};

	glGenVertexArrays(1, &renderer.VAO_font);
	glBindVertexArray(renderer.VAO_font);
	glGenVertexArrays(1, &renderer.VAO_texture_quad);
	glBindVertexArray(renderer.VAO_texture_quad);

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	u32 sol_col 		= 0;
	u32 textured_quad 	= 1;
	u32 font		= 2;

	CreateShader(&renderer.shader[sol_col], "..\\shader\\VS.h", GL_VERTEX_SHADER);
	CreateShader(&renderer.shader[sol_col], "..\\shader\\FS_solid_col.h", GL_FRAGMENT_SHADER);
	LinkShaderProgram(&renderer.shader[sol_col]);
	UseShaderProgram(&renderer.shader[sol_col]);


	CreateShader(&renderer.shader[textured_quad], "..\\shader\\VS.h", GL_VERTEX_SHADER);
	CreateShader(&renderer.shader[textured_quad], "..\\shader\\FS_Texture.h", GL_FRAGMENT_SHADER);
	LinkShaderProgram(&renderer.shader[textured_quad]);
	UseShaderProgram(&renderer.shader[textured_quad]);

	CreateShader(&renderer.shader[font], "..\\shader\\VS_text.h", GL_VERTEX_SHADER);
	CreateShader(&renderer.shader[font], "..\\shader\\FS_Font.h", GL_FRAGMENT_SHADER);
	LinkShaderProgram(&renderer.shader[font]);
	UseShaderProgram(&renderer.shader[font]);




	CJ_VTX_ATTRIB vbo_sprite_attrib[3];
	vbo_sprite_attrib[0].size = 2;
	vbo_sprite_attrib[0].type = GL_FLOAT;
	vbo_sprite_attrib[0].stride = 8;
	vbo_sprite_attrib[0].offset = 0;

	vbo_sprite_attrib[1].size = 4;
	vbo_sprite_attrib[1].type = GL_FLOAT;
	vbo_sprite_attrib[1].stride = 8;
	vbo_sprite_attrib[1].offset = 2;

	vbo_sprite_attrib[2].size = 2;
	vbo_sprite_attrib[2].type = GL_FLOAT;
	vbo_sprite_attrib[2].stride = 8;
	vbo_sprite_attrib[2].offset = 6;

	const u32 size = 4 * 32 * 1000 * sizeof(float);
	u8 *vertex_memory = (u8*)VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	glBindVertexArray(renderer.VAO_texture_quad);
	InitializeVBO(&renderer.vbo_sprite, vertex_memory, size, ArrayCount(vbo_sprite_attrib), vbo_sprite_attrib);

	u32 IBO = 0;
	const u32 quads = size / (sizeof(CJ_VTX_QUAD) * 4);
	u32 ibo_data[quads * 6] = {};
	renderer.ibo_sprite.base = ibo_data;
	renderer.ibo_sprite.size = sizeof(ibo_data);
	for(u32 i = 0; i < quads; i++)
	{
		ibo_data[0 + (i * 6)] = 0 + (i * 4);
		ibo_data[1 + (i * 6)] = 1 + (i * 4);
		ibo_data[2 + (i * 6)] = 2 + (i * 4);
		ibo_data[3 + (i * 6)] = 0 + (i * 4);
		ibo_data[4 + (i * 6)] = 2 + (i * 4);
		ibo_data[5 + (i * 6)] = 3 + (i * 4);
	}

	glGenBuffers(1, &IBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ibo_data), ibo_data, GL_DYNAMIC_DRAW);

	CJ_VTX_ATTRIB vbo_text_attrib[3];
	vbo_text_attrib[0].size = 2;
	vbo_text_attrib[0].type = GL_FLOAT;
	vbo_text_attrib[0].stride = 8;
	vbo_text_attrib[0].offset = 0;

	vbo_text_attrib[1].size = 4;
	vbo_text_attrib[1].type = GL_FLOAT;
	vbo_text_attrib[1].stride = 8;
	vbo_text_attrib[1].offset = 2;

	vbo_text_attrib[2].size = 2;
	vbo_text_attrib[2].type = GL_FLOAT;
	vbo_text_attrib[2].stride = 8;
	vbo_text_attrib[2].offset = 6;


	const u32 size_text = 512 * 8 * 6 * sizeof(float);
	u8 *vertex_memory_for_text = (u8*)VirtualAlloc(0, size_text, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	glBindVertexArray(renderer.VAO_font);
	InitializeVBO(&renderer.vbo_text, vertex_memory_for_text, size_text, ArrayCount(vbo_text_attrib), vbo_text_attrib);

	//LoadTexture(&renderer.texture_batch[0], "assets\\tileSheet32x32.png", LOAD_IMAGE, 0);
	LoadTexture(&renderer.texture_batch[1], "C:\\windows\\fonts\\cour.ttf", LOAD_FONT, 64);
	LoadTexture(&renderer.texture_batch[2], "C:\\windows\\fonts\\candara.ttf", LOAD_FONT, 30);

	return renderer;
}


void InvertSTBBuffer(unsigned char *memory, u32 w, u32 h)
{
    for(i32 y = 0; y < h / 2; y++)
    {
        for(i32 x = 0; x < w; x++)
        {
            i32 temp = memory[x + (y * w)];
            memory[x + (y * w)] = memory[x + ((h - 1) * w) - (y * w)];
            memory[x + ((h - 1) * w) - (y * w)] = temp;
        }
    }
}

void LoadTexture(TEXTUREINFO *tex_i, char *texture_path, u32 texture_type, i32 glyph_size)
{
	if(texture_type == LOAD_IMAGE) 
	{
	    
	    // Generating/binding and loading texture
	    i32 image_w = 0;
	    i32 image_h = 0;
	    i32 image_nChannels = 0;

	    if(tex_i->texture_id != 0)
	    {
		    glDeleteTextures(1, &tex_i->texture_id);
	    }
	    
	    glGenTextures(1, &tex_i->texture_id);
	    glBindTexture(GL_TEXTURE_2D, tex_i->texture_id);
	    
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	    
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	    
	    stbi_set_flip_vertically_on_load(true);
	    u8 *texture = stbi_load(texture_path, &image_w, &image_h, &image_nChannels, 0);
	    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_w, image_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
	    stbi_image_free(texture);
	    // End of gen/bind/load texture
	    
	    tex_i->texture_loaded = true;
	
	
	    
	} 
	else if (texture_type == LOAD_FONT)
	{
	    
	    if(tex_i->texture_id != 0)
	    {
		    glDeleteTextures(1, &tex_i->texture_id);
	    }

	    // Start gen/bind/load font texture atlas
	    glGenTextures(1, &tex_i->texture_id);
	    glBindTexture(GL_TEXTURE_2D, tex_i->texture_id);
	    
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	    
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
	    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	    
	    size_t bytes_read = 0;
	    u32 fontf_sz = 4 * tex_i->atlas_w * tex_i->atlas_h;
	    tex_i->font_file_data = (unsigned char*)calloc(fontf_sz, sizeof(unsigned char));
	    tex_i->pixels = (unsigned char*)calloc(fontf_sz, sizeof(unsigned char));
	    
	    bytes_read = fread(tex_i->font_file_data, 1, fontf_sz, fopen(texture_path, "rb"));
	    
	    // init pixel_bitmap and packing context
	    bool success = 0;
	    success = stbtt_PackBegin(&tex_i->pack_context, tex_i->pixels, tex_i->atlas_w, tex_i->atlas_h, 0, 1, 0);
	    
	    // set detail level of characters in font which requires the pixel_bitmap to be bigger
	    stbtt_PackSetOversampling(&tex_i->pack_context, 2, 2);
	    
	    // Set what characters in the pixel_map you want STBTT_POINT_SIZE makes the charcter size in tex_i->pixels
	    stbtt_PackFontRange(&tex_i->pack_context, tex_i->font_file_data, 0, STBTT_POINT_SIZE(glyph_size), ' ', 126,  tex_i->packed_char);
	    
	    // Invert font texture becuase stb give's it to us upside down ( y increases downward )
	    InvertSTBBuffer(tex_i->pixels, tex_i->atlas_w,  tex_i->atlas_h);
	    
	    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, tex_i->atlas_w, tex_i->atlas_h, 0, GL_RED, GL_UNSIGNED_BYTE, tex_i->pixels);
	    
	    free(tex_i->pixels);
	    free(tex_i->font_file_data);
	    stbtt_PackEnd(&tex_i->pack_context);
	    // End the packing context and free font_tex_ile and font atlas pixels
	    
	    tex_i->texture_loaded = true;
	}
    
}

void RenderClear(V4f col)
{
	glClearColor(col.x, col.y, col.z, col.w);
	glClear(GL_COLOR_BUFFER_BIT);

}

void DrawElements(CJ_RENDERER *renderer, u32 shader_index, u32 tex_index, u32 n_ents_to_draw, u32 n_ents_offset)
{

	UseShaderProgram(&renderer->shader[shader_index]);
	glBindTexture(GL_TEXTURE_2D, renderer->texture_batch[tex_index].texture_id);
	glDrawElements(GL_TRIANGLES, 6 * n_ents_to_draw, GL_UNSIGNED_INT, (const void*)(n_ents_offset * 6 * sizeof(u32))); 

}
#endif
Mārtiņš Možeiko
2201 posts / 1 project
Sort drawing by swapping indices in openGL indexbuffer
It gets messed up because you are storing pointers to array on stack. Once CreateRenderer() function returns it is not valid to access renderer.ibo_sprite.base data.
Simon Anciaux
1057 posts
Sort drawing by swapping indices in openGL indexbuffer
If I'm understanding your code correctly, in SwapOrderInIBO you don't update the entities ibo_id, so after the first swap the player entity doesn't points to the player indices.

I find it easier to sort the entities before creating the index and vertex buffers, so that actual indices value don't matter for the ordering.

1
2
3
4
5
6
game_update( );
sort_entities( );
for( entities ) {
    push_entity( );
}
render( );
90 posts
Sort drawing by swapping indices in openGL indexbuffer
mmozeiko
It gets messed up because you are storing pointers to array on stack. Once CreateRenderer() function returns it is not valid to access renderer.ibo_sprite.base data.


Thanks, that solved the glBufferSubdata()
90 posts
Sort drawing by swapping indices in openGL indexbuffer
mrmixer
If I'm understanding your code correctly, in SwapOrderInIBO you don't update the entities ibo_id, so after the first swap the player entity doesn't points to the player indices.

I find it easier to sort the entities before creating the index and vertex buffers, so that actual indices value don't matter for the ordering.

1
2
3
4
5
6
game_update( );
sort_entities( );
for( entities ) {
    push_entity( );
}
render( );



exactly the ibo_id is the same but the values are swapped.

Like this:

 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
renderer->ibo_sprite.base[player->ibo_id + 0] = 0;
renderer->ibo_sprite.base[player->ibo_id + 1] = 1;
renderer->ibo_sprite.base[player->ibo_id + 2] = 2;
renderer->ibo_sprite.base[player->ibo_id + 3] = 0;
renderer->ibo_sprite.base[player->ibo_id + 4] = 2;
renderer->ibo_sprite.base[player->ibo_id + 5] = 3;

renderer->ibo_sprite.base[monster->ibo_id + 0] = 4;
renderer->ibo_sprite.base[monster->ibo_id + 1] = 5;
renderer->ibo_sprite.base[monster->ibo_id + 2] = 6;
renderer->ibo_sprite.base[monster->ibo_id + 3] = 4;
renderer->ibo_sprite.base[monster->ibo_id + 4] = 6;
renderer->ibo_sprite.base[monster->ibo_id + 5] = 7;


this results in monster being drawn ontop of player.

if player y_pos < monster y_pos, swap occurs and we get this

renderer->ibo_sprite.base[player->ibo_id + 0] = 4;
renderer->ibo_sprite.base[player->ibo_id + 1] = 5;
renderer->ibo_sprite.base[player->ibo_id + 2] = 6;
renderer->ibo_sprite.base[player->ibo_id + 3] = 4;
renderer->ibo_sprite.base[player->ibo_id + 4] = 6;
renderer->ibo_sprite.base[player->ibo_id + 5] = 7;

renderer->ibo_sprite.base[monster->ibo_id + 0] = 0;
renderer->ibo_sprite.base[monster->ibo_id + 1] = 1;
renderer->ibo_sprite.base[monster->ibo_id + 2] = 2;
renderer->ibo_sprite.base[monster->ibo_id + 3] = 0;
renderer->ibo_sprite.base[monster->ibo_id + 4] = 2;
renderer->ibo_sprite.base[monster->ibo_id + 5] = 3;

this results in player being drawn ontop of monster.



this works in case 1 which is this
Only check player against monster

1
	SwapOrderInIBO(renderer, game->player, game->monster);



but with this case 2:
Check player against all tiles then,
check monster against all tiles then,
check player against monster.

even if the ibo_values are swapped i. e the player ibo_values at the corresponding indices in the index buffer
is bigger than the monster ibo_values at the corresponding indices, the monster get's draw ontop of the player.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
	u32 start_entity_id = game->tilemap.entity->id;
	u32 n_tiles = (game->tilemap.w  * game->tilemap.h);
	for(u32 i = 0; i < n_tiles; i++)
	{
		SwapOrderInIBO(renderer, game->player, &game->tilemap.entity[i]);
	}

	for(u32 i = 0; i < n_tiles; i++)
	{
		SwapOrderInIBO(renderer, game->monster, &game->tilemap.entity[i]);
	}

	if(game->player->pos.y < game->monster->pos.y)
	{
		if(renderer->ibo_sprite.base[game->player->ibo_id] < renderer->ibo_sprite.base[game->monster->ibo_id])
		{
		}
	}

	SwapOrderInIBO(renderer, game->player, game->monster);
Simon Anciaux
1057 posts
Sort drawing by swapping indices in openGL indexbuffer
Edited by Simon Anciaux on Reason: typo
 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
/*
Let's say that:
- player Y is 1;
- monster Y is 0;
- tile Y is 2.
- The vertex buffer has the player first, the monster second, and the tile third.
- The index buffer has the player first, the monster second, and the tile third.

We expect the final draw order to be (first drawn to last drawn):
- Tile;
- Player;
- Monster;
*/
vtx[ 18 ] = {
    x0, y0, x1, y1, x2, y2, /* Player triangle */ 
    x3, y3, x4, y4, x5, y5, /* Monster triangle */
    x6, y6, x7, y7, x8, y8 /* Tile triangle */
};

idx[ 9 ] = {
    0, 1, 2, /* Player indices */
    3, 4, 5, /* Monster indices */
    6, 7, 8 /* Tile indices */
};

u32 player_index_start = 0; /* entity->ibo_id */
u32 monster_index_start = 3;
u32 tile_index_start = 6;

/* Case 1: swapping player and monster only (no tile considered in this case)
   after the sort, idx would be, assuming a swap happened, which wouldn't
   be the case in this example, but this is just to point something. */
idx[ 9 ] = {
    3, 4, 5,
    0, 1, 2,
    6, 7, 8
};

/* Meaning, that the first triangle to be drawn is the one defined
   by indices 3, 4, 5, which is the monster triangle (x3, y3, x4, y4, x5, y5).
   But from this point on, (idx + player_index_start) points to the
   monster index data (3, 4, 5) and by consequence the monster vertices.
   Which is only OK if you don't refer to it afterwards.
   If you only do this swap, than it looks OK.
*/

/* Case 2: multiple swaps

   Player against tiles:
   - player Y is 1 < tile Y is 2
   - idx + player_index_start => (0, 1, 2)
   - idx + tile_index_start => (6, 7, 8) 
   => player indices < than tile indices so we make the swap.
*/
idx[ 9 ] = {
    6, 7, 8,
    3, 4, 5,
    0, 1, 2
};
/* Since we don't update player_index_start and tile_index_start
   They points at the incorrect indices for the next swaps.
   - idx + player_index_start => (6, 7, 8) which is the tile triangle (x6, y6, x7, y7, x8, y8)
   - idx + tile_index_start => (0, 1, 2) which is the player triangle (x0, y0, x1, y1, x2, y2)

   Monster against tile:
   This in practice compares indices from the monster against the player's.
   - idx + monster_index_start => (3, 4, 5)
   - idx + tile_index_start => (0, 1, 2)

   - Monster Y is 0 < than tile Y is 2
   - idx + monster_index_start => (3, 4, 5)
   - idx + tile_index_start => (0, 1, 2)
   => monster indices > than tile indices so no swap.
*/
idx[ 9 ] = {
    6, 7, 8,
    3, 4, 5,
    0, 1, 2
};
/* Swapping player and monster:
   - player Y is 1 > monster Y is 0
   - idx + player_index_start => (6, 7, 8)
   - idx + monster_index_start => (3, 4, 5)
   => player indices > monster indices so we swap
*/
idx[ 9 ] = {
    3, 4, 5,
    6, 7, 8,
    0, 1, 2
};
/* So the final draw order is Monster (Y=0), Tile (Y=2), Player (Y=1)
   Which means none of the triangle is in the correct order position. We expected
   Tile (Y=2), Player (Y=1), Monster (Y=0).
*/


One thing, you might not have understood is, as mmozeiko said, that the indices values don't matter on the draw order. What is important is the order of the indices in the index buffer. The first 3 indices in the buffer are always the first triangle to be drawn whatever their actual value is, the last 3 indices are always the last triangle drawn whatever their actual value is.

Updating the xxx_index_start (entity->ibo_id) variables might solve this (I'm not sure).

As I said in the previous reply, I think it would be easier, to sort entities based on their Y position, and push them to the vertex and index buffer after the sort. Relying on actual indices values seems harder and error prone to me.
90 posts
Sort drawing by swapping indices in openGL indexbuffer
Allright thanks a lot for that!

I realized now that i don't even have to touch the index buffer since you could just, like you said, send the entity_batch to the vertex buffer
in sorted order based on the y_pos!
90 posts
Sort drawing by swapping indices in openGL indexbuffer
Edited by C_Worm on
This would be a more correct way to do it right?

1. copy the entity batch into a separate batch
2. sort the batch based on y_pos
3 send the sorted batch to the vertex buffer.

Im only testing this with 2 entites but you could imagine doing some sorting on the whole batch
to get all entites sorted by the y_pos, which should work?




 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
void UpdateVBO_Entity(CJ_VBO *vbo, GAME_HANDLER *game)
{
	CJ_VTX_QUAD *pVertex = (CJ_VTX_QUAD*)vbo->base;
    
	assert(game->entity_used <= game->entity_batch_count);
    

	ENTITY sorted_batch[150] = {};
	memcpy(sorted_batch, game->entity_batch, sizeof(ENTITY) * game->entity_used);

	ENTITY *player 	= &sorted_batch[game->player->id];
	ENTITY *monster = &sorted_batch[game->monster->id];

	if(player->pos.y < monster->pos.y)
	{
		if(player->id < monster->id)
		{
			ENTITY temp = {};
			memcpy(&temp, player, sizeof(ENTITY));
			memcpy(player, monster, sizeof(ENTITY));
			memcpy(monster, &temp, sizeof(ENTITY));
		}
	}


	for(u32 i = 0; i < game->entity_used; i++)
	{
		V2f pos 	= sorted_batch[i].pos;
		V2f size 	= sorted_batch[i].size;
		V4f col 	= sorted_batch[i].col;
		V2f tCoord_p	= sorted_batch[i].texcoord_pos;
		V2f tCoord_sz	= sorted_batch[i].texcoord_size;
        
		pVertex[(i * 4) + 0].pos = v2f(pos.x, pos.y);
		pVertex[(i * 4) + 1].pos = v2f(pos.x, pos.y + size.y);
		pVertex[(i * 4) + 2].pos = v2f(pos.x + size.x, pos.y + size.y);
		pVertex[(i * 4) + 3].pos = v2f(pos.x + size.x, pos.y);
        
		pVertex[(i * 4) + 0].col = col;
		pVertex[(i * 4) + 1].col = col;
		pVertex[(i * 4) + 2].col = col;
		pVertex[(i * 4) + 3].col = col;
        
		pVertex[(i * 4) + 0].tCoord = v2f(tCoord_p.x, tCoord_p.y);
		pVertex[(i * 4) + 1].tCoord = v2f(tCoord_p.x, tCoord_p.y + tCoord_sz.y);
		pVertex[(i * 4) + 2].tCoord = v2f(tCoord_p.x + tCoord_sz.x, tCoord_p.y + tCoord_sz.y);
		pVertex[(i * 4) + 3].tCoord = v2f(tCoord_p.x + tCoord_sz.y, tCoord_p.y);
        
	}
    
}
Simon Anciaux
1057 posts
Sort drawing by swapping indices in openGL indexbuffer
If it's working, than it's up to you to decide if it's correct.

One remarks is that you don't need to use memcpy to copy a single struct, you can just use =. I believe that the compiler will choose what's the best way to do the copy based on the size of the struct (I may be wrong about that).

Depending on the size of the ENTITY struct and the number of entities, doing a full copy of the array just for the sort might become a performance concern (copying memory not used by the sort, and not using cache lines efficiently). If that happens, one possible solution is to create an array with only the information needed to perform the sort and get back to the correct entities later.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
r32 entity_ys[ max_entity_count ];
u32 entity_index[ max_entity_count ];

for ( umm i = 0; i < entity_count; i++ ) {
    entity_ys[ i ] = entities[ i ].pos.y;
    entity_index[ i ] = i;
}

/* The sorts work on entity_ys to choose what element comes first
   and each times it swaps, it also swaps in entity_index so that at
   the end, entity_index contains the entities index to render in the
   correct order.
*/
sort_entities_on_ys( entity_ys, entity_index, entity_count );

for ( umm i = 0; i < entity_count; i++ ) {
    render_push_entity( entities + entity_index[ i ] );
}

render( );