Dark Bit Factory & Gravity
GENERAL => Challenges & Competitions => Topic started by: Xalthorn on August 31, 2008
-
I've put [REDUCED RES] in the subject so it can be found easily and stays with the entrants, however its obviously not an actual entry :D
I said I'd throw this out to people once the compo had closed, so here it is. I put a lot of comments through the code but I'll briefly explain what it does as well. If it doesn't make sense, or I've just confused the issue, let me know and I'll try to explain what I was doing.
Okay, we have a 160x120 display area to draw on, but this drawing area is placed in the centre of a full sized buffer. It means that if we want to draw something that won't quite fit onto the drawing area, it can overflow without causing memory crashes or give unsightly X-wrapping as it flows to the next line. There is a dot(x,y,colour) function that handles this tidily for us.
We draw in shades of blue (or grey if you like, but only the blue portion is used) as all we're interested in is a single value from 0-192 (193-255 are pointless as they are the same as 192). This actually makes some routines easier as we don't have to prepare a full colour code, we can take a single byte value and throw it at the drawing area.
Once we've finished drawing our stuff, three routines take over. The first one, dither_screen() takes our 0-192 values and turns them into 0-3 values with appropriate dithering patterns.
After that is done, four_colour_screen() converts these 0-3 values into the appropriate rgb() value as determined by our preset colours C1, C2, C3, and C4.
Finally, buffer_convert() takes our little 160x120 drawing area and throws it out to the 640x480 displaybuffer.
These routines take over your colours and make sure that you are only ever drawing in four colours and also mean that you only have to process and draw pixels once, it does the scaling for you.
Sometimes you won't want dithering. In which case, just make sure you draw pixels using values of 0, 64, 128, and 192. If you're using photoshop (or something similar) to create images for inclusion in your code, only draw with greys or pure blues with the aforementioned values.
Of course, if you want a cute dithered picture from photoshop, just do a gradual grey gradient and let the routine handle it. I did this for the xalboy demo, and I've attached images to show what I mean. However, sometimes it doesn't work.... I was going to have a picture of a dragon and let the routine dither it, but at that resolution it just looked awful. I've attached the original picture to this post as well.
I've also attached the images I used for the speccy demo in case anyone wants to play with isometric stuff.
I'm going to tidy my stuff up a little and release my entries in full source with images, but that's something for tomorrow I think.
Ah well, I hope it's useful/informative to someone.
' ------------------------------------------------------------------------------
' Framework for a 160x120, 4 colour resolution
'
' ------------------------------------------------------------------------------
' ------------------------------------------------------------------------------
' Include any libraries and external code files that we want to use
' ------------------------------------------------------------------------------
' #define ptc_win
#Include Once "tinyptc.bi"
' Get ourselves set up for good coding practice
OPTION STATIC
OPTION EXPLICIT
' ------------------------------------------------------------------------------
' Set up any variables that will never change (they will stay constant)
' ------------------------------------------------------------------------------
' Our main display resolution
CONST XRES=640
CONST YRES=480
' The resolution of our actual drawing area
CONST DXRES=160
CONST DYRES=120
' An offset to the section that we're drawing on
CONST OFFX=240
CONST OFFY=180
' ------------------------------------------------------------------------------
' We need to set up a bunch of variables that can be used anywhere in the code
' ------------------------------------------------------------------------------
' Set up an array for our draw buffer
' This may confuse a little... why is it a full 640x480 buffer and not
' 160x120? This is because to make our lives so much easier, the 160x120
' area that we draw on will be placed right in the middle of a full sized
' screen. This means that we can 'overdraw' things without worrying about
' crashing right out of the buffer. It means you can draw images that
' do not need cropping to your viewport, it means you can smoothly scroll
' things in and out of the edges, and so on.
'
' An interesting thing to point out is the colours used in your actual
' drawing and plotting. Keep to either greys or blues (blues is easier)
' as only the blue channel is used to determine the brightness of a given
' pixel. We're essentially drawing in monochrome and applying a palette
' at the end. The simplest way to do this is to simply set colour values
' from 0-192. Values higher than 192 don't have any more effect than 192
' as the colour ranges map to the colours as shown below:
'
' 0 : first colour (C1)
' 64 : second colour (C2)
'128 : third colour (C3)
'192 : fourth colour (C4)
'
' Colours between these values will be automatically dithered by the
' dithering routine. So if you do a gradual shade from 0-192, you'll
' see the dithering in full effect.
'
' The little demo with the rectangles show the dithering as well. The
' rectangles are all given a colour that falls between the 'pure colours'
' and are therefore shaded.
DIM SHARED AS UINTEGER DRAWBUFFER (XRES*YRES)
' The display buffer, a nice, normal, full sized buffer. This is the
' buffer that is actually displayed to the screen.
DIM SHARED AS UINTEGER DISPLAYBUFFER (XRES*YRES)
' Four colour variables that are set with RGB(). They are used by the
' dithering routine to automatically display only four colours at a time.
' Don't use these to draw with, they're only used by the dithering routine.
DIM SHARED AS UINTEGER C1, C2, C3, C4
' This is an 8x8 bayer dither array that we will use to do the dithering
DIM SHARED PATTERN(64) AS UINTEGER
' ------------------------------------------------------------------------------
' Now to declare which subroutines we are going to write
' ------------------------------------------------------------------------------
DECLARE SUB DITHER_SCREEN()
DECLARE SUB BUFFER_CONVERT(byval sourcebuffer as uinteger ptr,byval targetbuffer as uinteger ptr)
DECLARE SUB FOUR_COLOUR_SCREEN()
DECLARE SUB DOT(byval X AS INTEGER, byval Y AS INTEGER, byval COL AS INTEGER)
DECLARE SUB INIT() :' Set up our variables and arrays
INIT()
' ------------------------------------------------------------------------------
' Now we're ready to get going, let's try and open a screen. If it fails, there
' is no point in continuing
' ------------------------------------------------------------------------------
IF ( PTC_OPEN ( "Framework", XRES, YRES ) = 0 ) THEN
END -1
END IF
' ------------------------------------------------------------------------------
' We have our variables and arrays set up, we have an open screen display, so
' we can get going.
' ------------------------------------------------------------------------------
' variables for the framework demonstration, remove these when you write your
' own stuff
DIM X,Y,BACKC,BACKCD AS INTEGER
BACKC=0
BACKCD=1
DIM NR AS INTEGER=5
DIM R,RX(NR),RY(NR),RXD(NR),RYD(NR),RC(NR),RW(NR),RH(NR) AS INTEGER
FOR X=0 TO NR-1
RX(X)=RND*160
RY(X)=RND*120
RXD(X)=(RND*4)-2:IF RXD(X)=0 THEN RXD(X)=1
RYD(X)=(RND*4)-2:IF RYD(X)=0 THEN RYD(X)=1
RW(X)=(RND*60)+20
RH(X)=(RND*60)+20
NEXT X
' five rectangles, four with pure colours, one with a dither pattern
' remember that we draw with shades of blue and the dither routine will convert
' the monochrome to our four colours with dithering.
RC(0)=0
RC(1)=64
RC(2)=128
RC(3)=192
RC(4)=100
DO
' First of all, clean up the drawbuffer
ERASE DRAWBUFFER
' This is where we would draw what we needed into the drawbuffer
' ---------------------------------------------------------------------
' Start of sample code, this should all be deleted when you want to do
' your own
' ---------------------------------------------------------------------
' Cycle the background colour
BACKC+=BACKCD
IF BACKC>254 THEN
BACKC=254
BACKCD=-1
END IF
IF BACKC<1 THEN
BACKC=1
BACKCD=1
END IF
' Fill the background with the colour and let the dithering routine
' work out what it should look like.
FOR X=0 TO 159
FOR Y=0 TO 119
DOT(X,Y,BACKC)
NEXT Y
NEXT X
' Draw the rectangles on the background and move them around
FOR R=0 TO NR-1
FOR X=RX(R) TO RX(R)+RW(R)
FOR Y=RY(R) TO RY(R)+RH(R)
DOT(X,Y,RC(R))
NEXT Y
NEXT X
RX(R)+=RXD(R)
RY(R)+=RYD(R)
IF RX(R)>160 OR RX(R)<-RW(R) THEN RXD(R)=-RXD(R)
IF RY(R)>120 OR RY(R)<-RH(R) THEN RYD(R)=-RYD(R)
NEXT R
' ---------------------------------------------------------------------
' End of sample code, this should all be deleted when you want to do
' your own
' ---------------------------------------------------------------------
' Now we dither the drawbuffer, leaving pixel values of 0, 1, 2, and 3 in
' a dithered pattern if appropriate.
DITHER_SCREEN()
' Now we convert those values into a proper RGB colour code as determined
' by our C1, C2, C3, and C4 variables
FOUR_COLOUR_SCREEN()
' Our buffer is now ready to be scaled up and placed on the displaybuffer
BUFFER_CONVERT(@DRAWBUFFER(0),@DISPLAYBUFFER(0))
' Now we display the display buffer
PTC_UPDATE@DISPLAYBUFFER(0)
' If you want to see what is happening with the drawbuffer, comment out
' the above line, and uncomment this one. It's good for debugging
' PTC_UPDATE@DRAWBUFFER(0)
LOOP UNTIL INKEY$=CHR$(27)
' We're all done now, we pressed escape, time to finish things up
END
PTC_CLOSE()
' ------------------------------------------------------------------------------
' DITHER_SCREEN
'
' Using the Bayer dither patter, run through the entire display area of the
' drawbuffer, applying the pattern where appropriate, which will leave us with
' an area of the screen with colour values of 0, 1, 2, and 3
' ------------------------------------------------------------------------------
SUB DITHER_SCREEN()
DIM X,Y,XX,YY,P,PP,PIX
FOR Y=0 TO DYRES-1
YY=Y MOD 8
FOR X=0 TO DXRES-1
XX=X MOD 8
PP=PATTERN(YY*8+XX)
P=(Y+OFFY)*XRES+X+OFFX
PIX=DRAWBUFFER(P)+PP
IF PIX>255 THEN PIX=255
PIX=PIX AND 192
PIX=PIX SHR 6
DRAWBUFFER(P)=PIX
NEXT X
NEXT Y
END SUB
' ------------------------------------------------------------------------------
' FOUR_COLOUR_SCREEN
'
' Running through the display area of the drawbuffer, convert all values of
' 0, 1, 2, and 3 to the appropriate colour values C1, C2, C3, C4. This won't
' really work unless you have already called DITHER_SCREEN()
' ------------------------------------------------------------------------------
SUB FOUR_COLOUR_SCREEN()
DIM P,X,Y,C AS UINTEGER
FOR Y=0 TO DYRES-1
FOR X=0 TO DXRES-1
P=(Y+OFFY)*XRES+X+OFFX
C=DRAWBUFFER(P)
IF C=0 THEN DRAWBUFFER(P)=C1
IF C=1 THEN DRAWBUFFER(P)=C2
IF C=2 THEN DRAWBUFFER(P)=C3
IF C=3 THEN DRAWBUFFER(P)=C4
NEXT X
NEXT Y
END SUB
' ------------------------------------------------------------------------------
' BUFFER_CONVERT
'
' Scale and write the drawbuffer to the displaybuffer
' ------------------------------------------------------------------------------
SUB BUFFER_CONVERT(byval sourcebuffer as uinteger ptr,byval targetbuffer as uinteger ptr)
DIM HEIGHT,LENGTH AS UINTEGER
LENGTH=160
HEIGHT=120
SOURCEBUFFER=SOURCEBUFFER+(OFFY*XRES)+OFFX
asm
' now we have done the fire buffer, throw the results back
mov esi, [SOURCEBUFFER]
mov edi, [TARGETBUFFER]
' load ax with the height for the yloop
mov ax, [HEIGHT]
BCYloop2:
push eax
' load BX with the screen width for the XLoop
mov bx,[LENGTH]
BCXloop2:
push ebx
mov eax,[esi]
mov [edi],eax
mov [edi+4],eax
mov [edi+8],eax
mov [edi+12],eax
mov [edi+2560],eax
mov [edi+2564],eax
mov [edi+2568],eax
mov [edi+2572],eax
mov [edi+5120],eax
mov [edi+5124],eax
mov [edi+5128],eax
mov [edi+5132],eax
mov [edi+7680],eax
mov [edi+7684],eax
mov [edi+7688],eax
mov [edi+7692],eax
add edi,16
add esi,4
' still running the xloop?
pop ebx
dec bx
jnz BCXloop2
add esi,1920
' still running the yloop?
add edi,7680
pop eax
dec ax
jnz BCYloop2
end asm
end sub
' ------------------------------------------------------------------------------
' DOT
'
' If it is safe to do so, draw a single dot into the drawbuffer. Use values
' from 0-159 for X and 0-119 for Y, the routine moves it to the correct
' position for you
' ------------------------------------------------------------------------------
SUB DOT(byval X AS INTEGER, byval Y AS INTEGER, byval C AS INTEGER)
IF X>=0 AND X<DXRES AND Y>=0 AND Y<DYRES THEN DRAWBUFFER(((Y+OFFY)*XRES)+OFFX+X)=C
END SUB
' ------------------------------------------------------------------------------
' INIT
'
' This is where we will initialise all of our variables and arrays. It is
' kept at the end of the code to keep the front tidy
' ------------------------------------------------------------------------------
SUB INIT()
DIM A AS INTEGER
' Let's read in the bayer dither pattern
FOR A=0 TO 63
READ PATTERN(A)
NEXT A
' Let's set the four colours that we intend to use. These variables are
' global, so you can set them anywhere you like in the program and know
' that you will never be displaying more than four colours.
C1=RGB(120, 0, 0)
C2=RGB( 0,120, 0)
C3=RGB( 0, 0,120)
C4=RGB(120,120, 0)
END SUB
' Bayer dither pattern
DATA 0,32, 8,40, 2,34,10,42
DATA 48,16,56,24,50,18,58,26
DATA 12,44, 4,36,14,46, 6,38
DATA 60,28,52,20,62,30,54,22
DATA 3,35,11,43, 1,33, 9,41
DATA 51,19,59,27,49,17,57,25
DATA 15,47, 7,39,13,45, 5,37
DATA 63,31,55,23,61,29,53,21
-
Thanks for this Xal :) Hope that lots of people give you karma for it! It's really nicely done and very well commented.
-
yes, karma++ :cheers:
-
inc K
-
Thanks a lot Xalthorn for releasing your framework. :buddies:
This will definitely help me in my learning process. :clap:
Cheers mate :cheers:
Special K to you
-
Thanks a lot Xalthorn for releasing your framework. :buddies:
This will definitely help me in my learning process. :clap:
Cheers mate :cheers:
Special K to you
Glad to help mate :D
-
Have some karma for being a man of your word :D. its great that you got this working as well as it does and even better that you chose to share it with everyone