Using frame buffering to solidify tft displayed objects

Status
Not open for further replies.
...

Declaring it as uint16_t will let it hold any value 0-65535. Any place that uses those stored values will need the same change.

Yes, you are learning! :cool: - the practical upshot of the underlined note is exactly as you show below! The Compiler will make note of any you miss, but that is what is needed. The stored data is still as it was - but the 'pointers / indexes / identifiers' whatever you want to call them need to be able to run from 0 toward 300 and it takes at least 16 bits to do that.

Note: You show two changed functions - I don't see 'before and after' so I'm not sure which once were changed. if it were me I'd make the initial change "const uint16_t tilemap[] " and then compile one time and scan the error/warning list in the compile output - it will tell you which ones need to be changed.

ok before I start running in circles trying to correct every thing, I would like to ask for a clarification......

if I change
Code:
const byte tilemap[] = {

to
Code:
const uint16_t tilemap[] = {

I will then need to go into my library and find the tilemap functions and change the uint8_t to uint16_t like so.....

Code:
void        drawTilemap(int x, int y, const uint16_t *tilemap, const uint8_t **spritesheet, const uint16_t * palette);
void        drawTilemap(int x, int y, const uint16_t *tilemap, const uint8_t **spritesheet, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, const uint16_t * palette);
 
Awesome thank you. Ive been doing a lot of reading but I still get stuck with some things. It was the const uint16_t *tilemap that needed to be changed in both functions. they were originally set to uint8_t.
 
Last edited:
ok what I'm trying to do is add an extra layer of tilemap on top of the current. The secondary tilemaps use bitmap people sprites with no backround so all I have to do is set the new on top which will be made of blank tles and people sprites with no background. they are all still using the writerectNbpp functions. All the bitmaps are also part of the same sprite collection.

I can add the extra tilemap to each room that needs it but I lose collision with the first layer and I can only collide with the bitmaps in the second layer or the people I added.

what could be causing that? I'm calling the tilemaps like so.....


Code:
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////seitch village1  
 else if(room == 8){
  tft.drawTilemap(cameraX, cameraY, seitchv1, spritesheet, palette);
  tft.drawTilemap(cameraX, cameraY, seitchv1a, spritesheet, palette);}
 
Not sure i understand. If this new area is a collision like all the others then if it is in the list and tested that area should be excluded the same. I saw a long list of compares somewhere for hit testing, if this is tested the same and stored the same it should work the same?
 
I'm confused as well. If I remove the second drawtilemap every thing works just fine. its only when I add the second tilemap. every thing else is done as before.
 
I have no idea what code is behind this - but wonder does drawtilemap start with a BLANK collision list on each call? So calling it twice in a row in some way overwrites the first call results? The second call would have to build on the first call and not start over for.
 
Hmmmmmmm i Was thinking.... maybe i could a second sprite sheet but it would still use the same collision function to name the solid tiles. I wouldnt know how to check about the blank part. How could i build the second call on top of the first?
 
I'd have to look at the drawtilemap and I don't know where it is or have any idea how it works. My guess is it enters an voids it. Pass a Boolean parameter to indicate if it should be voided or appended/updated?
 
umm still learning about serial prints what would you have me pass in the code?

Could it have something to do with how many sprites I'm trying call for the tilemap? since it is 260 total sprites. take a llok at this...

Code:
 void        drawBitmapTM(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *bitmap, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh,  uint16_t color);
	boolean     getBitmapPixel(const uint8_t* bitmap, uint16_t x, uint16_t y);

	void        drawTilemap(int x, int y, const uint16_t *tilemap, const uint8_t **spritesheet, const uint16_t * palette);
	void        drawTilemap(int x, int y, const uint16_t *tilemap, const uint8_t **spritesheet, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, const uint16_t * palette);

	typedef struct {       //line 171 "Public Variables   - ADD by Summoner123
		int x;                    //X coordinate                 - ADD by Summoner123
		int y;                    //Y coordinate                 - ADD by Summoner123
		const byte *spritecol;    //Sprite of object             - ADD by Summoner123
	}object;
	object solid[300];         // Matriz were saved a Sprite, X and Y cordinates of all tiles on the screen - ADD by Summoner123

	int numcolision = 3;     //count of solid objects indacat how many tiles drawed on the screen - ADD by Summoner123

	bool flagcollision = true;

its using byte for sprite collision. May be I should have to change that as well?
 
umm still learning about serial prints what would you have me pass in the code?

Could it have something to do with how many sprites I'm trying call for the tilemap? since it is 260 total sprites. take a llok at this...
...
its using byte for sprite collision. May be I should have to change that as well?

That doesn't show the drawTilemap() code - or link to it on github? I offered a possibly reasonable guess at the nature of the problem - until I could glance at the code I wouldn't be able to say more - and I say glance because of time constraints. This post either shows I'm off base on how it is done - or didn't register ...
 
I understand time constraints. I do appreciate the second look and help.

Here is the cpp part of the tilemap function if you don't mind taking a look. I understand what your saying just don't know how to instigate it. I did have though of calling the second one first but I think that would kill the first layer any way.

Code:
void GrafxT3::drawTilemap(int x, int y, const uint16_t *tilemap, const uint8_t **spritesheet, const uint16_t * palette){
	drawTilemap(x, y, tilemap, spritesheet, 0, 0, GrafxT3_TFTHEIGHT, GrafxT3_TFTWIDTH, palette);
}

void GrafxT3::drawTilemap(int x, int y, const uint16_t *tilemap, const uint8_t **spritesheet, uint16_t dx, uint16_t dy, uint16_t dw, uint16_t dh, const uint16_t * palette){
   uint8_t tilemap_width = pgm_read_byte(tilemap);
   uint8_t tilemap_height = pgm_read_byte(tilemap + 1);
   uint8_t tile_width = pgm_read_byte(tilemap + 2);
   uint8_t tile_height = pgm_read_byte(tilemap + 3);
   tilemap += 4; // now the first tiyleis at tilemap
// uint16_t tilemap_width = pgm_read_byte(tilemap)* 256 + pgm_read_byte(tilemap + 1);
// uint16_t tilemap_height = pgm_read_byte(tilemap + 2)* 256 + pgm_read_byte(tilemap + 3);
// uint16_t tile_width = pgm_read_byte(tilemap + 4);
// uint16_t tile_height = pgm_read_byte(tilemap + 5);
// tilemap += 6; // now the first tile is at tilemap

	uint16_t ddw = dw + dx;
	uint16_t ddh = dh + dy;
	uint16_t maxDdx = (dw - x + tile_width - 1) / tile_width;
	uint16_t maxDdy = (dh - y + tile_height - 1) / tile_height;
	if (tilemap_width < maxDdx){
		maxDdx = tilemap_width;
	}
	if (tilemap_height < maxDdy){
		maxDdy = tilemap_height;
	}
	int16_t startDdx = (-x) / tile_width;
	int16_t startDdy = (-y) / tile_height;
	if (startDdx < 0){
		startDdx = 0;
	}
	if (startDdy < 0){
		startDdy = 0;
	}
	if (flagcollision)numcolision = 0;                                 //Line 735 - clear numcolision - ADD by Summoner123

	for (uint16_t ddy = startDdy; ddy < maxDdy; ddy++){
		for (uint16_t ddx = startDdx; ddx < maxDdx; ddx++){
			int16_t drawX = ddx*tile_width + x + dx;
			int16_t drawY = ddy*tile_height + y + dy;
			uint16_t tile = pgm_read_byte(tilemap + ddy*tilemap_width + ddx);
			if (drawX >= dx && drawY >= dy && drawX <= (ddw - tile_width) && drawY <= (ddh - tile_height)){
				writeRectNBPP(drawX, drawY,tile_width, tile_height, 4, spritesheet[tile], palette );

				if (flagcollision){
					solid[numcolision].x = drawX;                     //Save X coordinate      - ADD by Summoner123
					solid[numcolision].y = drawY;                     //Save Y coordinate      - ADD by Summoner123
					solid[numcolision].spritecol = spritesheet[tile]; //Save Sprite of tile    - ADD by Summoner123
					numcolision++;                                    //Increment numcolision  - ADD by Summoner123
				}
			}
			else{ // we need to draw a partial bitmap
				writeRect4BPPtm(drawX, drawY, tile_width, tile_height, spritesheet[tile], dx, dy, dw, dh, palette);
			}
		}
	}
}
 
I don't really know what you need to see but I think I may have a solution that will cost less in the end.

So what I'm trying to do is add an extra layer of tilemap that has just the npc characters in it. It should work, I used it for the gamebuino library just fine. Any way I would like this.....

Code:
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////seitch village1  
 else if(room == 8){
  tft.drawTilemap(cameraX, cameraY, seitchv1, spritesheet, palette);
  tft.drawTilemap(cameraX, cameraY, seitchv1a, spritesheet, palette);}

As we know that kills the first layer objects collision. So instead of calling two expensive tilemaps we just call the one tilemap then add the npc characters like we would the player sprites.

Code:
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////seitch village1  
 else if(room == 8){
  tft.drawTilemap(cameraX, cameraY, seitchv1, spritesheet, palette);
  tft.writeRectNbpp(int x, int y, int w, int h, 4, bitmap_name, palette);}
 
I glanced at it and other than numcollisions=0 I didn't have time to make more sense of what it was doing - and now back to other things. Firewood, ordered new cell phones, got a box from Frank, other stuff to do . . .

But yes, doing it all in one pass - extending what you did before might be the better fix.
 
Im getting a lil heavy on sprites taking up space. This is off topic but.....

Can a header file be loaded onto an SD and read? For instance if my header file includes all bitmaps, can the ternsy read them and display them as needed?
 
Anything read from SD would have to reside in RAM for use - would that work?

Adding an SPI FLASH chip could provide megabytes of storage - though also needs to be pulled to RAM - but would be faster and perhaps part of the system.
 
Last edited:
Hmmmmmmm spi flash module would be great but it limits how many users would be able to play the game with out further modifying thier system then there's space and all that. Idk maybe I'll try to develop a board at some point with extra flash included. So all you would have to do is a lil soldering with the TFT and stuff. But that's a pipe dream and very far off.

I'm trying to learn about bit wise operators as way to move blocks in my game for puzzles and the chapter mentioned something about bit compression but was only a mention. Is there away we compress the bitmap data? Not like for tiles which need to be readily available but for stuff like monster bitmaps which only appear at random when I call a battle function.

I don't know that ram can hold all the bitmap data
 
all right guys I need a teacher right now. if you don't mind.........

I just got all my over world maps programmed and working. The problem is 9 times 150 tile by 150 tile maps with all the dungeon and village entrances and exits is costing me 42% of storage. 256 total tiles that does not include the actual dungeon and village tilemaps or the people to go in. so I really need to learn how I can save space. how can I use all the available storage types contained with in the teensy3.6 including the sd to ease some of the bitmap storage in flash. New technique? any thing? I will read links!
 
Last edited:
Easiest way to test the concept might be to buy a PJRC PROP_LC with FLASH on it. It is not directly addressable as noted - data stored there would need to be pulled into RAM for use - if not pushed straight to the screen buffer. But the T_3.6 SD card would be similar for testing - except not the same to read/write.

The only other thing would be to test compressibility - but unless you could decompress right to the screen buffer - they too would need to reside in RAM for manipulation.

Given that I kept seeing ProgMem() in the code - perhaps pushing straight to the display buffer would not be a problem? It seems you might be able to do a wrapper for the Flash or SD access or even compressed in Teensy_Flash that could act the same where you stored the object with an identifier passed to the wrapper - that would : find, open and read/decompress the desired bitmap to a pointer in RAM/Screen buffer.

You said 150x150 - are those full two byte values?
 
I'm using the 4bpp method. All 16 colors are being used to display the tiles

ok big problem...... I accidently broke off my pin #12 or the miso pin. is there any way I can assign miso to another pin?


edit::: ok I see that I can use spi.setmiso to change the miso pin to its alyernate position but where would be the best place to add that lil snippet of code?
 
Last edited:
On the card you can see pins 8 and 39 have a GRAY MISO0 - so either should be directly usable with software change for full function.

I was just wondering about the math - so 150x150x2 is 45000 bytes per tilemap. Depending on the repetition found for lossless compression based on the method used they might shrink. They would have to be read and un-compressed for use - not unlike reading from FLASH or SD card or perhaps ProgMem as noted.

I don't see notes on a library for compression on a quick look.
 
I don't see why cant use the sd as an extension of the flash.

I have an implementation question...... I put together an esp32 game unit using the library y'all helped me with so I can get more familiar with it and so I can help the users out who want to use that board. I also need a new soldering iron to fix my t3.6. I would love to use bounce but its missing a whole chain of files needed to do that. Stuff like wprogram and integer types and yada yada. Any way I'm back to my old button program for it. of course its not working and I think it has to do with needing to update the buttons constantly.

I have a fix for that here in the updateAll function that was left over from the original libraries that's been going unused....

Code:
boolean Grafx_esp::updateAll() {
	if (((nextFrameMillis - millis()) > timePerFrame) && frameEndMicros) { //if time to render a new frame is reached and the frame end has ran once
		nextFrameMillis = millis() + timePerFrame;
		frameCount++;

		frameEndMicros = 0;
		frameStartMicros = micros();


		buttons.update();

		return true;

	}
	else {
		if (!frameEndMicros) { //runs once at the end of the frame
//			sound.updateTrack();
//			sound.updatePattern();
//			sound.updateNote();
//			updatePopup();
//			displayBattery();

			updateScreen(); //send the buffer to the screen
			if (!persistence)
				freeFrameBuffer(); //clear the buffer

			frameEndMicros = micros(); //measure the frame's end time
			frameDurationMicros = frameEndMicros - frameStartMicros;

			//            display.setTextColor(BLACK);
			//            display.setCursor(0, 40);
			//            display.print(frameDurationMicros / timePerFrame);
			//            display.print(" ");
			//            display.print(2048 - freeRam());

			//            display.setCursor(0, 32);
			//            display.print("CPU:");
			//            display.print(frameDurationMicros / timePerFrame);
			//            display.println("/1000");
			//            display.print("RAM:");
			//            display.print(2048 - freeRam());
			//            display.println("/2048");
		}
		return false;
	}
}

So originally I would call it like this for a single sketch page program....

example 1
Code:
//updates the GameRIot (the display, the sound, the buttons, everyyhing)
  //returns true when it's time to render a new frame (20 times/second)
   if(tft.updateAll()){
    if (tft.buttons.repeat(BTN_RIGHT,1));//{X--:}
    if (tft.buttons.repeat(BTN_LEFT,1));//{X++:}
    if (tft.buttons.repeat(BTN_DOWN,1));//{Y--:}
    if (tft.buttons.repeat(BTN_UP,1));//{Y--:}


//tft.drawTilemap(x, y, tilemap1, spritesheet1, BLACK);
//  tft.drawTilemap(x, y, tilemap2, spritesheet2, BROWN);
 //  tft.drawTilemap(x, y, tilemap3, spritesheet3, LIGHTBROWN);

    if (tft.buttons.repeat(BTN_UP,1)){
       tft.drawBitmap1(player_x, player_y,paul_rearblack,16,16,BLACK);
         tft.drawBitmap1(player_x, player_y,paul_rearblue,16,16,BLUE);
          tft.drawBitmap1(player_x, player_y,paul_rearbrown,16,16,BROWN);
          tft.drawBitmap1(player_x, player_y,paul_reargrey,16,16,GREY);
           tft.drawBitmap1(player_x, player_y,paul_rearpink,16,16,PINK);
            tft.drawBitmap1(player_x, player_y,paul_rearred,16,16,YELLOW);

            player_direction = 1;
             player_y = player_y - 1;}
            if(player_y <= 0){
              player_y = 0;}

but since I have changed the structure of the program so much I'm unsure where to put the update. should I just add updateAll() with in the void loop with update screen and void player?

example 2
Code:
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////Loop////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void loop(void) {
  
updateAll();
drawplayer();
///drawbattle(player_x, player_y);
  tft.updateScreen();

}

Or should I wrap the player functions inside like the 1st example above.
 
Last edited:
Is there a reason for the teensy bounce library using wprogram over Arduino.h? I'm curious because I want to use my bounce set up with the other board to keep me busy while I'm waiting to fix my t36.
 
Last edited:
ok guys I hate to keep bothering youwith the esp but you guys are the only ones that know exactly how this library works. I have it set up almost exactly like the t3 but I'm having trouble with my color. when the display prints its printing out weird colors instead of the chosen palette colors.

any idea as to what could be causing this?
 
Last edited:
Also the player is stuck in vertical mode meaning i can only move him up or down. The buttons work but left and right only makes him go up and down. Heres a video....


https://github.com/Duhjoker/Dune_esp32

Could it have anything to do with the way I added the transparent pixels to the screen in the function WriteRectNBPP()

Code:
void Grafx_esp::writeRectNBPP(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t bits_per_pixel,
	const uint8_t *pixels, const uint16_t * palette)
{
	//Serial.printf("\nWR8: %d %d %d %d %x\n", x, y, w, h, (uint32_t)pixels);
	x += _originx;
	y += _originy;
	uint8_t pixels_per_byte = 8 / bits_per_pixel;
	uint16_t count_of_bytes_per_row = (w + pixels_per_byte - 1) / pixels_per_byte;		// Round up to handle non multiples
	uint8_t row_shift_init = 8 - bits_per_pixel;				// We shift down 6 bits by default 
	uint8_t pixel_bit_mask = (1 << bits_per_pixel) - 1; 		// get mask to use below
	// Rectangular clipping 

	// See if the whole thing out of bounds...
	if ((x >= _displayclipx2) || (y >= _displayclipy2)) return;
	if (((x + w) <= _displayclipx1) || ((y + h) <= _displayclipy1)) return;

	// In these cases you can not do simple clipping, as we need to synchronize the colors array with the
	// We can clip the height as when we get to the last visible we don't have to go any farther. 
	// also maybe starting y as we will advance the color array. 
	// Again assume multiple of 8 for width
	if (y < _displayclipy1) {
		int dy = (_displayclipy1 - y);
		h -= dy;
		pixels += dy * count_of_bytes_per_row;
		y = _displayclipy1;
	}

	if ((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;

	// For X see how many items in color array to skip at start of row and likewise end of row 
	if (x < _displayclipx1) {
		uint16_t x_clip_left = _displayclipx1 - x;
		w -= x_clip_left;
		x = _displayclipx1;
		// Now lets update pixels to the rigth offset and mask
		uint8_t x_clip_left_bytes_incr = x_clip_left / pixels_per_byte;
		pixels += x_clip_left_bytes_incr;
		row_shift_init = 8 - (x_clip_left - (x_clip_left_bytes_incr * pixels_per_byte) + 1) * bits_per_pixel;
	}

	if ((x + w - 1) >= _displayclipx2) {
		w = _displayclipx2 - x;
	}

	const uint8_t * pixels_row_start = pixels;  // remember our starting position offset into row

#ifdef ENABLE_Grafx_FRAMEBUFFER
	if (_use_fbtft) {
		for (; h>0; h--) {
			uint16_t * pfbPixel = mapYtoFBPtr(y) + x;
			pixels = pixels_row_start;				// setup for this row
			uint8_t pixel_shift = row_shift_init;			// Setup mask

			for (int i = 0; i < w; i++) {
///				*pfbPixel++ = FBmapColor(palette[((*pixels) >> pixel_shift) & pixel_bit_mask]);
				uint8_t palette_index = ((*pixels)>>pixel_shift) & pixel_bit_mask;
                if (palette_index != TRANSPARENT_INDEX)
                *pfbPixel = palette[palette_index];
                *pfbPixel++;
				if (!pixel_shift) {
					pixel_shift = 8 - bits_per_pixel;	//setup next mask
					pixels++;
				}
				else {
					pixel_shift -= bits_per_pixel;
				}
			}
			y++;
			pixels_row_start += count_of_bytes_per_row;
		}
		return;

	}
#endif
/*
	beginSPITransaction();
	setAddr(x, y, x + w - 1, y + h - 1);
	writecommand_cont(Grafx_RAMWR);
	for (; h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask

		for (int i = 0; i < w; i++) {
			writedata16_cont(palette[((*pixels) >> pixel_shift) & pixel_bit_mask]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			}
			else {
				pixel_shift -= bits_per_pixel;
			}
		}
		pixels_row_start += count_of_bytes_per_row;
	}
	writecommand_last(Grafx_NOP);
	endSPITransaction();
}*/
////////////////////////////////////////////////////////////////////
///for transparent pixels
	beginSPITransaction();
	for (;h>0; h--) {
		pixels = pixels_row_start;				// setup for this row
		uint8_t pixel_shift = row_shift_init;			// Setup mask
                int16_t x_out = x; 
		for (int i = 0 ;i < w; i++) {
			uint8_t palette_index = ((*pixels)>>pixel_shift) & pixel_bit_mask;
			if (palette_index != TRANSPARENT_INDEX)
				Pixel(x_out, y, palette[palette_index]);
			if (!pixel_shift) {
				pixel_shift = 8 - bits_per_pixel;	//setup next mask
				pixels++;
			} else {
				pixel_shift -= bits_per_pixel;
			}
			x_out++;
		}
		pixels_row_start += count_of_bytes_per_row;
		y++;
	}
	endSPITransaction();
}
 
Last edited:
Status
Not open for further replies.
Back
Top