From e3bdbaf1715bbd2082591a0830f1759d91065d3a Mon Sep 17 00:00:00 2001 From: Slyvtt Date: Fri, 4 Mar 2022 21:58:14 +0100 Subject: [PATCH] Update Rendering for Speed --- .gitignore | 13 ++ CMakeLists.txt | 42 +++++ CppOutRun.cbp | 46 +++++ CppOutRun.layout | 50 ++++++ README.md | 45 +++++ assets-cg/fxconv-metadata.txt | 5 + assets-cg/icon-sel.png | Bin 0 -> 15424 bytes assets-cg/icon-uns.png | Bin 0 -> 11866 bytes assets-cg/traffic/car1.png | Bin 0 -> 924 bytes assets-cg/traffic/car2.png | Bin 0 -> 984 bytes assets-cg/traffic/car3.png | Bin 0 -> 1014 bytes assets-cg/traffic/car4.png | Bin 0 -> 950 bytes assets-cg/traffic/car5.png | Bin 0 -> 968 bytes assets-cg/traffic/car6.png | Bin 0 -> 1023 bytes assets-cg/traffic/car7.png | Bin 0 -> 925 bytes assets-cg/traffic/car8.png | Bin 0 -> 922 bytes assets-cg/traffic/fxconv-metadata.txt | 5 + build | 3 + clean | 2 + src/fixed.h | 86 +++++++++ src/include/camera.h | 26 +++ src/include/circuit.h | 12 ++ src/include/drawstuff.h | 9 + src/include/segment.h | 26 +++ src/main.cc | 243 ++++++++++++++++++++++++++ src/parameters.h | 24 +++ src/src/camera.cc | 54 ++++++ src/src/circuit.cc | 117 +++++++++++++ src/src/drawstuff.cc | 94 ++++++++++ src/src/segment.cc | 46 +++++ 30 files changed, 948 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CppOutRun.cbp create mode 100644 CppOutRun.layout create mode 100644 README.md create mode 100644 assets-cg/fxconv-metadata.txt create mode 100644 assets-cg/icon-sel.png create mode 100644 assets-cg/icon-uns.png create mode 100644 assets-cg/traffic/car1.png create mode 100644 assets-cg/traffic/car2.png create mode 100644 assets-cg/traffic/car3.png create mode 100644 assets-cg/traffic/car4.png create mode 100644 assets-cg/traffic/car5.png create mode 100644 assets-cg/traffic/car6.png create mode 100644 assets-cg/traffic/car7.png create mode 100644 assets-cg/traffic/car8.png create mode 100644 assets-cg/traffic/fxconv-metadata.txt create mode 100755 build create mode 100755 clean create mode 100644 src/fixed.h create mode 100644 src/include/camera.h create mode 100644 src/include/circuit.h create mode 100644 src/include/drawstuff.h create mode 100644 src/include/segment.h create mode 100644 src/main.cc create mode 100644 src/parameters.h create mode 100644 src/src/camera.cc create mode 100644 src/src/circuit.cc create mode 100644 src/src/drawstuff.cc create mode 100644 src/src/segment.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c4f84b --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Build files +/build-fx +/build-cg +/*.g1a +/*.g3a + +# Python bytecode + __pycache__/ + +# Common IDE files +*.sublime-project +*.sublime-workspace +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bf91817 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +# Configure with [fxsdk build-cg], which provide the +# toolchain file and module path of the fxSDK + +cmake_minimum_required(VERSION 3.15) +project(MyAddin) + +include(GenerateG3A) +include(Fxconv) +find_package(Gint 2.7.0 REQUIRED) +find_package(LibProf 2.4 REQUIRED) + +set(SOURCES + src/main.cc + src/src/segment.cc + src/src/camera.cc + src/src/circuit.cc + src/src/drawstuff.cc + # ... +) + +set(ASSETS_cg +# assets-cg/traffic/car1.png +# assets-cg/traffic/car2.png +# assets-cg/traffic/car3.png +# assets-cg/traffic/car4.png +# assets-cg/traffic/car5.png +# assets-cg/traffic/car6.png +# assets-cg/traffic/car7.png +# assets-cg/traffic/car8.png +) + +fxconv_declare_assets(${ASSETS_cg} WITH_METADATA) + +add_executable(myaddin ${SOURCES} ${ASSETS_${FXSDK_PLATFORM}}) +target_compile_options(myaddin PRIVATE -Wall -Wextra -Os -std=c++11 -c -fno-rtti -fno-use-cxa-atexit -fpermissive) +target_link_libraries(myaddin LibProf::LibProf Gint::Gint -lustl -lc) +target_link_options(myaddin PRIVATE -Wl,-Map=Build_Addin.map) + +if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50) + generate_g3a(TARGET myaddin OUTPUT "OutRunFP.g3a" VERSION 00.100.0000 + NAME "OutRunFP" ICONS assets-cg/icon-uns.png assets-cg/icon-sel.png) +endif() diff --git a/CppOutRun.cbp b/CppOutRun.cbp new file mode 100644 index 0000000..259bbe9 --- /dev/null +++ b/CppOutRun.cbp @@ -0,0 +1,46 @@ + + + + + + diff --git a/CppOutRun.layout b/CppOutRun.layout new file mode 100644 index 0000000..03b2e38 --- /dev/null +++ b/CppOutRun.layout @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..de31dc3 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# OutRun : a simple race game for Casio Graph 90+E + +OutRun is a very simple race game based on the classical Outrun-type license, very famous in the 90's. +The purpose of the game is to drive as fast as possible on various highways, in the middle of the traffic. + + +## Installing and compiling the game + +Provided you would like to compile the game Addin, starting from the code, some prerequities are mandatory : + - `fxlibc` in its `@dev` version is mandatory (to get correct implementation of ) + - `µSTL` is also mandatory + - `libprof` is also needed to get accurate timing + +**Installing with GiteaPC** + +`fxlibc` can be installed using GiteaPC by using + +```Bash +% giteapc install Vhex-Kernel-Core/fxlibc@dev +``` + +`µSTL` can then be installed by typing in a terminal + +```Bash +% giteapc install Slyvtt/uSTL_2.3 +``` + +`libprof` can be built and installed with + +```bash +% giteapc install Lephenixnoir/libprof +``` + + +**Compiling** + +Simply run `fxsdk build-cg`. The fxSDK will invoke CMake with a suitable toolchain file while exposing CMake modules for the calculator. + +```bash +% fxsdk build-cg +``` + +Please note that currently there is no compile target for `fx-9860G` + + diff --git a/assets-cg/fxconv-metadata.txt b/assets-cg/fxconv-metadata.txt new file mode 100644 index 0000000..fc1422a --- /dev/null +++ b/assets-cg/fxconv-metadata.txt @@ -0,0 +1,5 @@ +#*.png: +# type: bopti-image +# profile: p4 +# name_regex: (.*)\.png \1 + diff --git a/assets-cg/icon-sel.png b/assets-cg/icon-sel.png new file mode 100644 index 0000000000000000000000000000000000000000..44b700647e364c0370f13b863035236d61334c4e GIT binary patch literal 15424 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 literal 0 HcmV?d00001 diff --git a/assets-cg/traffic/car7.png b/assets-cg/traffic/car7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e0b59013118884073bd237523f74d513845a25f GIT binary patch literal 925 zcmV;O17iG%P)EX>4Tx04R}tkv&MmKpe$iQ>7vmhjx(S5V1O05EXIMDionYs1;guFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?K>DYS_3;J6>}?mh0_0YbgZG^=YI&~)2O zCE{WxyDA1=As~VfhA=EM%b1g-Bs|C0J$!tC`-Nm{=@yu+qV-Xlle$#8Fk#DPPFA zta9Gstd*;*bx;1nU`}6I<~q$mB(R7jND!f*iW17O5u;Tn#X^eq;~xG<(=U-rAy)~E z91EyGgWU9k|H1EWt^DMKn-q)#-7k*wF#-g3fkw@7zKZvz+CZB5w&E_Z-|Cqp*nrsSt7sJF5Ty02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00DJLL_t(&-tC&p4FVw$hQWCBvZYvnomjypR%2oXc47gR z;>G)T*pRprU||>@oBaR9m?%5Ue#`@~Mqgyw%4!aFFp!#ey(FCO%jfI*sA~A-IU8e) z`QCKr20|3PNG(ZFAjg7`Mue`WjHF+`MQ zc$DTuj8>tBvLUzElYKx)Y79vtW3LL$>pab?SOP+jcHt0lmdUM5cdbz63PL=Dc*)4k zE}=Z{#?=zHPnr$6eQsP3f}jeAz#(u*jVuF!ka)KnsuxB(yDe2{Ck=JT79q7&y2I9L zUj~HK$}(F$i(8|)y-n)>w7PQ;vPB5L8I*9oA?i}4U1_QKyf|4Pq<5t%B?OgfZ$cvH z7Aa%=tM^UjtotqeI|zw*mz3U6Zxzi0gfRU8VlEX>4Tx04R}tkv&MmKpe$iQ>7vmhjx(S5V1O05EXIMDionYs1;guFuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?K>DYS_3;J6>}?mh0_0YbgZG^=YI&~)2O zCE{WxyDA1=As~VfhA=EM%b1g-Bs|C0J$!tC`-Nm{=@yu+qV-Xlle$#8Fk#DPPFA zta9Gstd*;*bx;1nU`}6I<~q$mB(R7jND!f*iW17O5u;Tn#X^eq;~xG<(=U-rAy)~E z91EyGgWU9k|H1EWt^DMKn-q)#-7k*wF#-g3fkw@7zKZvz+CZB5w&E_Z-|Cqp*nrsSt7eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00DAIL_t(&-tCx65`r)chNMhXt~w)` zmLw&#MgJ@j(xmy!D-8ZZLtP1;$}R)M{dQMGJm*(c2X4#c>JtcYyiNKhA`z*olCUFy zkVb@%5D_w-K}Oqh`pJV75V8>kZKO6MbYMl(>L;W9tM@<%lWS?8(YmS_stzHCI9C~J zaAwQdD1Z=4mEk#p&nCU+j$AI`_UHN)5Tflvq^b2RRmM6cgAl0?vF{8blAG(N zK!~+dm2RB>t|caZ_g)^W=i_`Y(xZJY=>;K)%u1SmgGZ7}jBG2h+L!YegjjQkbo2bZ zTn@PzA|<2)7R5D;P|ginFIlWSc?v?P~KzaT{FLyn$N z_D;vO>;WMXA!pgDdsQjqkkUS+w>?KK0U@obOmBiJvzL%xBSu>_K}abfs51ZFhwzP( wV#5?V_bo}j +typedef int64_t fixed_t; +//extended from initial int32_t + +/* Standard arithmetic. */ + +static inline fixed_t fmul(fixed_t left, fixed_t right) +{ + /* Generally optimized by the compiler to use dmuls.l and xtrct */ + int64_t p = (int64_t)left * (int64_t)right; + return (int32_t)(p >> 16); +} + +static inline fixed_t fdiv(fixed_t left, fixed_t right) +{ + /* Pretty slow */ + int64_t d = (int64_t)left << 16; + return d / right; +} + +#define fix(x) ((int)((x) * 65536)) + +static inline fixed_t fixdouble(long double constant) +{ + return (fixed_t)(constant * 65536); +} + +static inline fixed_t fixfloat(float constant) +{ + return (fixed_t)(constant * 65536); +} + +static inline fixed_t fdec(fixed_t f) +{ + return f & 0xffff; +} + +static inline int ffloor(fixed_t f) +{ + return f >> 16; +} + +static inline int fceil(fixed_t f) +{ + return (f + 0xffff) >> 16; +} + +static inline int fround(fixed_t f) +{ + return (f + 0x8000) >> 16; +} + +static inline float f2float(fixed_t f) +{ + return (float)f / 65536; +} + +static inline double f2double(fixed_t f) +{ + return (double)f / 65536; +} + +static inline double f2int(fixed_t f) +{ + return (int)f / 65536; +} + +static inline fixed_t feasein(fixed_t x) +{ + return fmul(x, x); +} + +static inline fixed_t fease(fixed_t x) +{ + if(x <= fix(0.5)) { + return 2 * fmul(x, x); + } + else { + x = fix(1) - x; + return fix(1) - 2 * fmul(x, x); + } +} diff --git a/src/include/camera.h b/src/include/camera.h new file mode 100644 index 0000000..62c527d --- /dev/null +++ b/src/include/camera.h @@ -0,0 +1,26 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "../fixed.h" + +class camera +{ + public: + camera(); + camera( double x, double y, double z); + ~camera(); + + void incX( double dx ); + void incY( double dy ); + void incZ( double dz ); + + void decX( double dx ); + void decY( double dy ); + void decZ( double dz ); + + fixed_t cX = 0; + fixed_t cY = 0; + fixed_t cZ = 0; +}; + +#endif // CAMERA_H diff --git a/src/include/circuit.h b/src/include/circuit.h new file mode 100644 index 0000000..6816b7f --- /dev/null +++ b/src/include/circuit.h @@ -0,0 +1,12 @@ +#include + +void initData( void ); +void createCircuit( void ); + +void projectCircuitFP( void ); +void projectCircuitFP( uint16_t index ); + +void printCircuit( void ); +void printCircuit( int i ); + +void drawCircuitSegment( uint16_t index ); diff --git a/src/include/drawstuff.h b/src/include/drawstuff.h new file mode 100644 index 0000000..5bb4992 --- /dev/null +++ b/src/include/drawstuff.h @@ -0,0 +1,9 @@ +#include +#include + +void gint_dhline(int x1, int x2, int y, uint16_t color); +void drawPolygon( int x1min, int x1max, int y1, int x2min, int x2max, int y2, uint8_t R, uint8_t G, uint8_t B, uint8_t A ); +void drawGrass( int y1, int y2, uint8_t R, uint8_t G, uint8_t B, uint8_t A ); + +void drawSky( void ); +void drawSky( int ymin, int ymax ); diff --git a/src/include/segment.h b/src/include/segment.h new file mode 100644 index 0000000..c6e507a --- /dev/null +++ b/src/include/segment.h @@ -0,0 +1,26 @@ +#ifndef SEGMENT_H +#define SEGMENT_H + +#include "../fixed.h" +#include +#include "camera.h" + +class Segment +{ + public: + Segment(); + Segment( uint16_t n_seg ); + ~Segment(); + + void Project3DFP( camera* c ); + + uint16_t Index=0; + uint8_t Curve=0; + + fixed_t wX=0, wY=0, wZ=0; + fixed_t sX=0, sY=0, sW=0; + fixed_t Scale=0; + int X=0,Y=0,W=0; +}; + +#endif // SEGMENT_H diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..52d81c9 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,243 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +#include "include/camera.h" +#include "include/segment.h" +#include "parameters.h" +#include "include/circuit.h" +#include "include/drawstuff.h" + + + +//extern bopti_image_t car1, car2, car3, car4, car5, car6, car7, car8; + + +std::vector circuit; +camera *cam; + + +bool stop = false; +bool record = false; +bool screenshot = false; + +uint16_t currentcurve=0; +uint8_t shiftcolor=0; + +float speed = 0; +float maxspeedforward = 8; +float maxspeedbackward = 4; +int direction = 1; +bool speedcontrol = false; + + +static void get_inputs( float dt ) +{ + key_event_t ev; + while((ev = pollevent()).type != KEYEV_NONE) + { + + } + speedcontrol = false; + + if(keydown(KEY_LEFT)) cam->decX(25.0); + if(keydown(KEY_RIGHT)) cam->incX(25.0); + + if(keydown(KEY_SHIFT)) + { + if (direction==-1 && speed > 0) + { + direction = -1; + speed -= 0.5; + if (speed<0) speed=0; + cam->decZ(speed*dt); + } + else + { + direction = 1; + speed+=0.05; + if (speed>maxspeedforward) speed=maxspeedforward; + cam->incZ(speed*dt); + } + + speedcontrol = true; + } + if(keydown(KEY_ALPHA)) + { + if (direction==1 && speed > 0) + { + direction = 1; + speed -= 0.5; + if (speed<0) speed=0; + cam->incZ(speed*dt); + } + else + { + direction = -1; + speed+=0.025; + if (speed>maxspeedbackward) speed=maxspeedbackward; + cam->decZ(speed*dt); + } + + speedcontrol = true; + } + + //if(keydown(KEY_7)) cam->incY(10.0); + //if(keydown(KEY_1)) cam->decY(10.0); + //if(keydown(KEY_MENU)) gint_osmenu(); + + if(keydown(KEY_EXIT)) stop = true; + + if(keydown(KEY_F6)) record = !record; + if(keydown(KEY_F4)) screenshot = true; + + if (speedcontrol==false) + { + speed-=0.3; + if (speed<0) speed=0; + + if (direction==1) + cam->incZ(speed*dt); + else + cam->decZ(speed*dt); + } +} + + +int main(void) +{ + __printf_enable_fp(); + __printf_enable_fixed(); + + usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL }; + usb_open(interfaces, GINT_CALL_NULL); + + prof_t perf_create, perf_project, perf_render; + uint32_t time_create=0, time_project=0, time_render=0; + prof_init(); + + int nbInterestingSegments = (MAX_RENDER_DISTANCE / SEGMENT_LENGTH) + 10; // the number of segments to be projected considering the rendering distance + + //-------------- + + initData( ); + + //-------------- + + perf_create = prof_make(); + prof_enter(perf_create); + + createCircuit(); + + prof_leave(perf_create); + time_create = prof_time(perf_create); + + //-------------- + + int indexstart = 0; + uint32_t maxDistance = (MAX_SEGMENT-nbInterestingSegments-5)*SEGMENT_LENGTH; + + uint32_t dt=0; + uint16_t l=0; + + while (!stop) + { + get_inputs( dt ); + dt = ((float) (time_render+time_project) / 1000.0); + + //-------------- + if (fround(cam->cZ)<=0) cam->cZ=fixdouble(0.0); + if (fround(cam->cZ)>=maxDistance) cam->cZ=fixdouble(maxDistance); + + indexstart = (fround(cam->cZ) - 400) / SEGMENT_LENGTH; + // there is an offset equals to 400 on z position + // this is to compute the first index of segment to be projected to screen + + if (indexstart<0) indexstart=0; + if (indexstart>MAX_SEGMENT-nbInterestingSegments-5-1) indexstart=MAX_SEGMENT-nbInterestingSegments-5-1; + + + //-------------- + + perf_project = prof_make(); + prof_enter(perf_project); + + for (int k=indexstart; kcX ) ); + dprint( 300, 15, C_WHITE, "CamY= %d", fround( cam->cY ) ); + dprint( 300, 29, C_WHITE, "CamZ= %d", fround( cam->cZ ) ); + + dprint( 280, 55, C_WHITE, "Dir = %d", direction ); + dprint( 280, 69, C_WHITE, "Spd = %.1f", speed ); + dprint( 280, 83, C_WHITE, "Dz = %.1f", speed*dt ); + dprint( 280, 97, C_WHITE, "dt = %.3D ms", dt ); + + //r61524_display(gint_vram, 0, DHEIGHT, R61524_DMA_WAIT); + dupdate(); + + prof_leave(perf_render); + time_render = prof_time(perf_render); + + if (screenshot && usb_is_open()) + { + usb_fxlink_screenshot(true); + screenshot = false; + } + + if(record && usb_is_open()) + { + usb_fxlink_videocapture(false); + } + + l++; + } + + prof_quit(); + usb_close(); + + circuit.clear(); + delete cam; + + return 1; +} diff --git a/src/parameters.h b/src/parameters.h new file mode 100644 index 0000000..8f1f6f4 --- /dev/null +++ b/src/parameters.h @@ -0,0 +1,24 @@ +#ifndef PARAMETERS_H +#define PARAMETERS_H + + +#define SCREEN_WIDTH 396 +#define SCREEN_HEIGHT 224 + +#define SCREEN_CX 198 +#define SCREEN_CY 112 + +#define SEGMENT_LENGTH 200 +#define ROAD_WIDTH 600 + +#define ASPECT_RATIO 1.767 +#define DISTANCE_SCREEN 0.83444 + +#define MAX_RENDER_DISTANCE 1500 + +#define MAX_SEGMENT 10000 + +#define std ustl + + +#endif // PARAMETERS_H diff --git a/src/src/camera.cc b/src/src/camera.cc new file mode 100644 index 0000000..74b2bbf --- /dev/null +++ b/src/src/camera.cc @@ -0,0 +1,54 @@ +#include "../include/camera.h" + +camera::camera() +{ + //ctor +} + +camera::camera( double x, double y, double z) +{ + cX = fixdouble( x ); + cY = fixdouble( y ); + cZ = fixdouble( z ); +} + + +camera::~camera() +{ + //dtor +} + + +void camera::incX( double dx ) +{ + cX += fixdouble( dx ); +}; + + +void camera::incY( double dy ) +{ + cY += fixdouble( dy ); +}; + + +void camera::incZ( double dz ) +{ + cZ += fixdouble( dz ); +}; + +void camera::decX( double dx ) +{ + cX -= fixdouble( dx ); +}; + + +void camera::decY( double dy ) +{ + cY -= fixdouble( dy ); +}; + + +void camera::decZ( double dz ) +{ + cZ -= fixdouble( dz ); +}; diff --git a/src/src/circuit.cc b/src/src/circuit.cc new file mode 100644 index 0000000..dbf5eab --- /dev/null +++ b/src/src/circuit.cc @@ -0,0 +1,117 @@ +#include "../include/circuit.h" +#include "../include/segment.h" +#include "../include/camera.h" +#include "../parameters.h" +#include + +#include "../include/drawstuff.h" + + +extern std::vector circuit; +extern camera *cam; +extern uint16_t currentcurve; +extern uint8_t shiftcolor; + + +void initData( void ) +{ + cam = new camera(); + cam->cX = fixdouble(0.0f); + cam->cY = fixdouble(300.0f); + cam->cZ = fixdouble(260.0f); +} + + +void createCircuit( void ) +{ + for( int i=0; iProject3DFP( cam ); + } +}; + + +void projectCircuitFP( uint16_t index ) +{ + circuit[index]->Project3DFP( cam ); +}; + + +void printCircuit( void ) +{ + for( int i=0; i<20; i++) + { + dprint(198, 1+10*(i), C_RED, "%d-(sX,Y,W)=(%d,%d,%d)", i, circuit[i]->X, circuit[i]->Y, circuit[i]->W ); + //dprint(1, 1+10*(i), C_GREEN, "%d-(_sY,Y,W)=(%.0f,%.0f,%.0f)", i, circuit[i]->_sX, circuit[i]->_sY, circuit[i]->_sW ); + } +}; + +void printCircuit( int i ) +{ + dprint(1, 1+10*(i), C_RED, "%d-(wX,Y,Z)=(%d,%d,%d)", i, fround(circuit[i]->wX), fround(circuit[i]->wY), fround(circuit[i]->wZ) ); + dprint(198, 1+10*(i), C_RED, "%d-(sX,Y,W)=(%d,%d,%d)", i, circuit[i]->X, circuit[i]->Y, circuit[i]->W ); +}; + + +void drawCircuitSegment( uint16_t index ) +{ + if (index>=circuit.size()-1) return; + + int X1 = (int) circuit[index]->X; + int Y1 = (int) circuit[index]->Y; + int W1 = (int) circuit[index]->W; + int X2 = (int) circuit[(index+1)]->X + circuit[index]->Curve; + int Y2 = (int) circuit[(index+1)]->Y; + int W2 = (int) circuit[(index+1)]->W; + + if (Y1==Y2) return; + + if (index%2==0) + { + drawGrass( Y2, Y1, 0, 255-shiftcolor, 45-shiftcolor, 255 ); + + // route + drawPolygon( X2-W2+currentcurve, X2+W2+currentcurve, Y2, X1-W1+currentcurve, X1+W1+currentcurve, Y1, 196-shiftcolor, 196-shiftcolor, 196-shiftcolor, 255 ); + + // ligne blanche centrale + drawPolygon( X2-W2/50+currentcurve, X2+W2/50+currentcurve, Y2, X1-W1/50+currentcurve, X1+W1/50+currentcurve, Y1, 255-shiftcolor, 255-shiftcolor, 255-shiftcolor, 255 ); + // ligne blanche gauche + drawPolygon( X2-W2/16-W2+currentcurve, X2-W2+currentcurve, Y2, X1-W1/16-W1+currentcurve, X1-W1+currentcurve, Y1, 255-shiftcolor, 255-shiftcolor, 255-shiftcolor, 255 ); + + // ligne blanche centrale gauche + drawPolygon( X2-W2/50-W2/2+currentcurve, X2+W2/50-W2/2+currentcurve, Y2, X1-W1/50-W1/2+currentcurve, X1+W1/50-W1/2+currentcurve, Y1, 255-shiftcolor, 255-shiftcolor, 255-shiftcolor, 255 ); + + // ligne blanche centrale + drawPolygon( X2-W2/50+currentcurve, X2+W2/50+currentcurve, Y2, X1-W1/50+currentcurve, X1+W1/50+currentcurve, Y1, 255-shiftcolor, 255-shiftcolor, 255-shiftcolor, 255 ); + + // ligne blanche centrale droite + drawPolygon( X2-W2/50+W2/2+currentcurve, X2+W2/50+W2/2+currentcurve, Y2, X1-W1/50+W1/2+currentcurve, X1+W1/50+W1/2+currentcurve, Y1, 255-shiftcolor, 255-shiftcolor, 255-shiftcolor, 255 ); + + // ligne blanche droite + drawPolygon( X2+W2+currentcurve, X2+W2+W2/16+currentcurve, Y2, X1+W1+currentcurve, X1+W1+W1/16+currentcurve, Y1, 255-shiftcolor, 255-shiftcolor, 255-shiftcolor, 255 ); + + } + else + { + + drawGrass( Y2, Y1, 0, 255-shiftcolor, 0, 255 ); + + drawPolygon( X2-W2+currentcurve, X2+W2+currentcurve, Y2, X1-W1+currentcurve, X1+W1+currentcurve, Y1, 180-shiftcolor, 180-shiftcolor, 180-shiftcolor, 255 ); + // ligne rouge gauche + drawPolygon( X2-W2/16-W2+currentcurve, X2-W2+currentcurve, Y2, X1-W1/16-W1+currentcurve, X1-W1+currentcurve, Y1, 255-shiftcolor, 0, 0, 255 ); + // ligne rouge droite + drawPolygon( X2+W2+currentcurve, X2+W2+W2/16+currentcurve, Y2, X1+W1+currentcurve, X1+W1+W1/16+currentcurve, Y1, 255-shiftcolor, 0, 0, 255 ); + + } + currentcurve += circuit[index]->Curve; +} diff --git a/src/src/drawstuff.cc b/src/src/drawstuff.cc new file mode 100644 index 0000000..9fd1786 --- /dev/null +++ b/src/src/drawstuff.cc @@ -0,0 +1,94 @@ +#include "../include/drawstuff.h" +#include "../parameters.h" +#include +#include "../fixed.h" + +void gint_dhline(int x1, int x2, int y, uint16_t color) +{ + if((uint)y >= 224) return; + if(x1 > x2) swap(x1, x2); + if(x1 >= 396 || x2 < 0) return; + if(x1 < 0) x1 = 0; + if(x2 >= 396) x2 = 395; + + int offset = 396 * y; + + gint_vram[offset + x1] = color; + gint_vram[offset + x2] = color; + + x1 = x1 + (x1 & 1); + x2 = (x2 + 1) & ~1; + + uint32_t *start = (void *)(gint_vram + offset + x1); + uint32_t *end = (void *)(gint_vram + offset + x2); + uint32_t op = (color << 16) | color; + + while(end > start) *--end = op; +}; + + +void drawGrass( int y1, int y2, uint8_t R, uint8_t G, uint8_t B, uint8_t A ) +{ + uint16_t color = C_RGB(R,G,B); + for (int y=y1; y<=y2; y++) gint_dhline( 0, SCREEN_WIDTH, y, color ); +}; + + +void drawPolygon( int x1min, int x1max, int y1, int x2min, int x2max, int y2, uint8_t R, uint8_t G, uint8_t B, uint8_t A ) +{ +/* + uint16_t color = C_RGB(R,G,B); + float Ddeltay = 1.0f / (float) (y2-y1); + float deltay=0.0f; + + for (int y=y1; y<=y2; y++) + { + if (y>0 && y0 && ycX); + fixed_t DY = (wY - c->cY); + fixed_t divDZ = fdiv( fix(1), (wZ - c->cZ)); + fixed_t RW = fix(ROAD_WIDTH); + + fixed_t divAR = fdiv(fix(1), fixdouble(ASPECT_RATIO)); + + Scale = fmul(fixdouble(DISTANCE_SCREEN), divDZ); + + fixed_t tempx = fmul(fmul(DX,Scale), divAR); + fixed_t tempy = fmul(DY, Scale); + fixed_t tempw = fmul(fmul(RW,Scale), divAR); + + sX=fmul(fix(SCREEN_CX), (fix(1)+tempx)); + sY=fmul(fix(SCREEN_CY), (fix(1)-tempy)); + sW=fmul(fix(SCREEN_CX), tempw); + + X=fround(sX); + Y=fround(sY); + W=fround(sW); +}