12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810 |
- #ifndef SMALL3DLIB_H
- #define SMALL3DLIB_H
- /*
- Simple realtime 3D software rasterization renderer. It is fast, focused on
- resource-limited computers, located in a single C header file, with no
- dependencies, using only 32bit integer arithmetics.
- author: Miloslav Ciz
- license: CC0 1.0 (public domain)
- found at https://creativecommons.org/publicdomain/zero/1.0/
- + additional waiver of all IP
- version: 0.852d
- Before including the library, define S3L_PIXEL_FUNCTION to the name of the
- function you'll be using to draw single pixels (this function will be called
- by the library to render the frames). Also either init S3L_resolutionX and
- S3L_resolutionY or define S3L_RESOLUTION_X and S3L_RESOLUTION_Y.
- You'll also need to decide what rendering strategy and other settings you
- want to use, depending on your specific usecase. You may want to use a
- z-buffer (full or reduced, S3L_Z_BUFFER), sorted-drawing (S3L_SORT), or even
- none of these. See the description of the options in this file.
- The rendering itself is done with S3L_drawScene, usually preceded by
- S3L_newFrame (for clearing zBuffer etc.).
- The library is meant to be used in not so huge programs that use single
- translation unit and so includes both declarations and implementation at once.
- If you for some reason use multiple translation units (which include the
- library), you'll have to handle this yourself (e.g. create a wrapper, manually
- split the library into .c and .h etc.).
- --------------------
- This work's goal is to never be encumbered by any exclusive intellectual
- property rights. The work is therefore provided under CC0 1.0 + additional
- WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
- intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
- INTELLECTUAL PROPERTY RGHTS is as follows:
- Each contributor to this work agrees that they waive any exclusive rights,
- including but not limited to copyright, patents, trademark, trade dress,
- industrial design, plant varieties and trade secrets, to any and all ideas,
- concepts, processes, discoveries, improvements and inventions conceived,
- discovered, made, designed, researched or developed by the contributor either
- solely or jointly with others, which relate to this work or result from this
- work. Should any waiver of such right be judged legally invalid or
- ineffective under applicable law, the contributor hereby grants to each
- affected person a royalty-free, non transferable, non sublicensable, non
- exclusive, irrevocable and unconditional license to this right.
- --------------------
- CONVENTIONS:
- This library should never draw pixels outside the specified screen
- boundaries, so you don't have to check this (that would cost CPU time)!
- You can safely assume that triangles are rasterized one by one and from top
- down, left to right (so you can utilize e.g. various caches), and if sorting
- is disabled the order of rasterization will be that specified in the scene
- structure and model arrays (of course, some triangles and models may be
- skipped due to culling etc.).
- Angles are in S3L_Units, a full angle (2 pi) is S3L_FRACTIONS_PER_UNITs.
- We use row vectors.
- In 3D space, a left-handed coord. system is used. One spatial unit is split
- into S3L_FRACTIONS_PER_UNIT fractions (fixed point arithmetic).
- y ^
- | _
- | /| z
- | /
- | /
- [0,0,0]-------> x
- Untransformed camera is placed at [0,0,0], looking forward along +z axis. The
- projection plane is centered at [0,0,0], stretrinch from
- -S3L_FRACTIONS_PER_UNIT to S3L_FRACTIONS_PER_UNIT horizontally (x),
- vertical size (y) depends on the aspect ratio (S3L_RESOLUTION_X and
- S3L_RESOLUTION_Y). Camera FOV is defined by focal length in S3L_Units.
- y ^
- | _
- | /| z
- ____|_/__
- | |/ |
- -----[0,0,0]-|-----> x
- |____|____|
- |
- |
- Rotations use Euler angles and are generally in the extrinsic Euler angles in
- ZXY order (by Z, then by X, then by Y). Positive rotation about an axis
- rotates CW (clock-wise) when looking in the direction of the axis.
- Coordinates of pixels on the screen start at the top left, from [0,0].
- There is NO subpixel accuracy (screen coordinates are only integer).
- Triangle rasterization rules are these (mostly same as OpenGL, D3D etc.):
- - Let's define:
- - left side:
- - not exactly horizontal, and on the left side of triangle
- - exactly horizontal and above the topmost
- (in other words: its normal points at least a little to the left or
- completely up)
- - right side: not left side
- - Pixel centers are at integer coordinates and triangle for drawing are
- specified with integer coordinates of pixel centers.
- - A pixel is rasterized:
- - if its center is inside the triangle OR
- - if its center is exactly on the triangle side which is left and at the
- same time is not on the side that's right (case of a triangle that's on
- a single line) OR
- - if its center is exactly on the triangle corner of sides neither of which
- is right.
- These rules imply among others:
- - Adjacent triangles don't have any overlapping pixels, nor gaps between.
- - Triangles of points that lie on a single line are NOT rasterized.
- - A single "long" triangle CAN be rasterized as isolated islands of pixels.
- - Transforming (e.g. mirroring, rotating by 90 degrees etc.) a result of
- rasterizing triangle A is NOT generally equal to applying the same
- transformation to triangle A first and then rasterizing it. Even the number
- of rasterized pixels is usually different.
- - If specifying a triangle with integer coordinates (which we are), then:
- - The bottom-most corner (or side) of a triangle is never rasterized
- (because it is connected to a right side).
- - The top-most corner can only be rasterized on completely horizontal side
- (otherwise it is connected to a right side).
- - Vertically middle corner is rasterized if and only if it is on the left
- of the triangle and at the same time is also not the bottom-most corner.
- */
- #include <stdint.h>
- #ifdef S3L_RESOLUTION_X
- #ifdef S3L_RESOLUTION_Y
- #define S3L_MAX_PIXELS (S3L_RESOLUTION_X * S3L_RESOLUTION_Y)
- #endif
- #endif
- #ifndef S3L_RESOLUTION_X
- #ifndef S3L_MAX_PIXELS
- #error Dynamic resolution set (S3L_RESOLUTION_X not defined), but\
- S3L_MAX_PIXELS not defined!
- #endif
- uint16_t S3L_resolutionX = 512; /**< If a static resolution is not set with
- S3L_RESOLUTION_X, this variable can be
- used to change X resolution at runtime,
- in which case S3L_MAX_PIXELS has to be
- defined (to allocate zBuffer etc.)! */
- #define S3L_RESOLUTION_X S3L_resolutionX
- #endif
- #ifndef S3L_RESOLUTION_Y
- #ifndef S3L_MAX_PIXELS
- #error Dynamic resolution set (S3L_RESOLUTION_Y not defined), but\
- S3L_MAX_PIXELS not defined!
- #endif
- uint16_t S3L_resolutionY = 512; /**< Same as S3L_resolutionX, but for Y
- resolution. */
- #define S3L_RESOLUTION_Y S3L_resolutionY
- #endif
- /** Units of measurement in 3D space. There is S3L_FRACTIONS_PER_UNIT in one
- spatial unit. By dividing the unit into fractions we effectively achieve a
- fixed point arithmetic. The number of fractions is a constant that serves as
- 1.0 in floating point arithmetic (normalization etc.). */
- typedef int32_t S3L_Unit;
- /** How many fractions a spatial unit is split into. This is NOT SUPPOSED TO
- BE REDEFINED, so rather don't do it (otherwise things may overflow etc.). */
- #define S3L_FRACTIONS_PER_UNIT 512
- typedef int16_t S3L_ScreenCoord;
- typedef uint16_t S3L_Index;
- #ifndef S3L_NEAR_CROSS_STRATEGY
- /** Specifies how the library will handle triangles that partially cross the
- near plane. These are problematic and require special handling. Possible
- values:
- 0: Strictly cull any triangle crossing the near plane. This will make such
- triangles disappear. This is good for performance or models viewed only
- from at least small distance.
- 1: Forcefully push the vertices crossing near plane in front of it. This is
- a cheap technique that can be good enough for displaying simple
- environments on slow devices, but texturing and geometric artifacts/warps
- will appear.
- 2: Geometrically correct the triangles crossing the near plane. This may
- result in some triangles being subdivided into two and is a little more
- expensive, but the results will be geometrically correct, even though
- barycentric correction is not performed so texturing artifacts will
- appear. Can be ideal with S3L_FLAT.
- 3: NOT IMPLEMENTED YET
- Perform both geometrical and barycentric correction of triangle crossing
- the near plane. This is significantly more expensive but results in
- correct rendering.
- */
- #define S3L_NEAR_CROSS_STRATEGY 0
- #endif
- #ifndef S3L_FLAT
- /** If on, disables computation of per-pixel values such as barycentric
- coordinates and depth -- these will still be available but will be the same
- for the whole triangle. This can be used to create flat-shaded renders and
- will be a lot faster. With this option on you will probably want to use
- sorting instead of z-buffer. */
- #define S3L_FLAT 0
- #endif
- #if S3L_FLAT
- #define S3L_COMPUTE_DEPTH 0
- #define S3L_PERSPECTIVE_CORRECTION 0
- // don't disable z-buffer, it makes sense to use it with no sorting
- #endif
- #ifndef S3L_PERSPECTIVE_CORRECTION
- /** Specifies what type of perspective correction (PC) to use. Remember this
- is an expensive operation! Possible values:
-
- - 0: No perspective correction. Fastest, inaccurate from most angles.
- - 1: Per-pixel perspective correction, accurate but very expensive.
- - 2: Approximation (computing only at every S3L_PC_APPROX_LENGTHth pixel).
- Quake-style approximation is used, which only computes the PC after
- S3L_PC_APPROX_LENGTH pixels. This is reasonably accurate and fast. */
- #define S3L_PERSPECTIVE_CORRECTION 0
- #endif
- #ifndef S3L_PC_APPROX_LENGTH
- /** For S3L_PERSPECTIVE_CORRECTION == 2, this specifies after how many pixels
- PC is recomputed. Should be a power of two to keep up the performance.
- Smaller is nicer but slower. */
- #define S3L_PC_APPROX_LENGTH 32
- #endif
- #if S3L_PERSPECTIVE_CORRECTION
- #define S3L_COMPUTE_DEPTH 1 // PC inevitably computes depth, so enable it
- #endif
- #ifndef S3L_COMPUTE_DEPTH
- /** Whether to compute depth for each pixel (fragment). Some other options
- may turn this on automatically. If you don't need depth information, turning
- this off can save performance. Depth will still be accessible in
- S3L_PixelInfo, but will be constant -- equal to center point depth -- over
- the whole triangle. */
- #define S3L_COMPUTE_DEPTH 1
- #endif
- #ifndef S3L_Z_BUFFER
- /** What type of z-buffer (depth buffer) to use for visibility determination.
- Possible values:
- - 0: Don't use z-buffer. This saves a lot of memory, but visibility checking
- won't be pixel-accurate and has to mostly be done by other means
- (typically sorting).
- - 1: Use full z-buffer (of S3L_Units) for visibiltiy determination. This is
- the most accurate option (and also a fast one), but requires a big
- amount of memory.
- - 2: Use reduced-size z-buffer (of bytes). This is fast and somewhat
- accurate, but inaccuracies can occur and a considerable amount of memory
- is needed. */
- #define S3L_Z_BUFFER 0
- #endif
- #ifndef S3L_REDUCED_Z_BUFFER_GRANULARITY
- /** For S3L_Z_BUFFER == 2 this sets the reduced z-buffer granularity. */
- #define S3L_REDUCED_Z_BUFFER_GRANULARITY 5
- #endif
- #ifndef S3L_STENCIL_BUFFER
- /** Whether to use stencil buffer for drawing -- with this a pixel that would
- be resterized over an already rasterized pixel (within a frame) will be
- discarded. This is mostly for front-to-back sorted drawing. */
- #define S3L_STENCIL_BUFFER 0
- #endif
- #ifndef S3L_SORT
- /** Defines how to sort triangles before drawing a frame. This can be used to
- solve visibility in case z-buffer is not used, to prevent overwriting already
- rasterized pixels, implement transparency etc. Note that for simplicity and
- performance a relatively simple sorting is used which doesn't work completely
- correctly, so mistakes can occur (even the best sorting wouldn't be able to
- solve e.g. intersecting triangles). Note that sorting requires a bit of extra
- memory -- an array of the triangles to sort -- the size of this array limits
- the maximum number of triangles that can be drawn in a single frame
- (S3L_MAX_TRIANGES_DRAWN). Possible values:
- - 0: Don't sort triangles. This is fastest and doesn't use extra memory.
- - 1: Sort triangles from back to front. This can in most cases solve
- visibility without requiring almost any extra memory compared to
- z-buffer.
- - 2: Sort triangles from front to back. This can be faster than back to
- front, because we prevent computing pixels that will be overwritten by
- nearer ones, but we need a 1b stencil buffer for this (enable
- S3L_STENCIL_BUFFER), so a bit more memory is needed. */
- #define S3L_SORT 0
- #endif
- #ifndef S3L_MAX_TRIANGES_DRAWN
- /** Maximum number of triangles that can be drawn in sorted modes. This
- affects the size of the cache used for triangle sorting. */
- #define S3L_MAX_TRIANGES_DRAWN 128
- #endif
- #ifndef S3L_NEAR
- /** Distance of the near clipping plane. Points in front or EXATLY ON this
- plane are considered outside the frustum. This must be >= 0. */
- #define S3L_NEAR (S3L_FRACTIONS_PER_UNIT / 4)
- #endif
- #if S3L_NEAR <= 0
- #define S3L_NEAR 1 // Can't be <= 0.
- #endif
- #ifndef S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE
- /** Affects the S3L_computeModelNormals function. See its description for
- details. */
- #define S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE 6
- #endif
- #ifndef S3L_FAST_LERP_QUALITY
- /** Quality (scaling) of SOME (stepped) linear interpolations. 0 will most
- likely be a tiny bit faster, but artifacts can occur for bigger tris, while
- higher values can fix this -- in theory all higher values will have the same
- speed (it is a shift value), but it mustn't be too high to prevent
- overflow. */
- #define S3L_FAST_LERP_QUALITY 11
- #endif
- /** Vector that consists of four scalars and can represent homogenous
- coordinates, but is generally also used as Vec3 and Vec2 for various
- purposes. */
- typedef struct
- {
- S3L_Unit x;
- S3L_Unit y;
- S3L_Unit z;
- S3L_Unit w;
- } S3L_Vec4;
- #define S3L_logVec4(v)\
- printf("Vec4: %d %d %d %d\n",((v).x),((v).y),((v).z),((v).w))
- static inline void S3L_initVec4(S3L_Vec4 *v);
- static inline void S3L_setVec4(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y,
- S3L_Unit z, S3L_Unit w);
- static inline void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added);
- static inline void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted);
- S3L_Unit S3L_vec3Length(S3L_Vec4 v);
- /** Normalizes Vec3. Note that this function tries to normalize correctly
- rather than quickly! If you need to normalize quickly, do it yourself in a
- way that best fits your case. */
- void S3L_normalizeVec3(S3L_Vec4 *v);
- /** Like S3L_normalizeVec3, but doesn't perform any checks on the input vector,
- which is faster, but can be very innacurate or overflowing. You are supposed
- to provide a "nice" vector (not too big or small). */
- static inline void S3L_normalizeVec3Fast(S3L_Vec4 *v);
- S3L_Unit S3L_vec2Length(S3L_Vec4 v);
- void S3L_crossProduct(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result);
- static inline S3L_Unit S3L_dotProductVec3(S3L_Vec4 a, S3L_Vec4 b);
- /** Computes a reflection direction (typically used e.g. for specular component
- in Phong illumination). The input vectors must be normalized. The result will
- be normalized as well. */
- void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result);
- /** Determines the winding of a triangle, returns 1 (CW, clockwise), -1 (CCW,
- counterclockwise) or 0 (points lie on a single line). */
- static inline int8_t S3L_triangleWinding(
- S3L_ScreenCoord x0,
- S3L_ScreenCoord y0,
- S3L_ScreenCoord x1,
- S3L_ScreenCoord y1,
- S3L_ScreenCoord x2,
- S3L_ScreenCoord y2);
- typedef struct
- {
- S3L_Vec4 translation;
- S3L_Vec4 rotation; /**< Euler angles. Rortation is applied in this order:
- 1. z = by z (roll) CW looking along z+
- 2. x = by x (pitch) CW looking along x+
- 3. y = by y (yaw) CW looking along y+ */
- S3L_Vec4 scale;
- } S3L_Transform3D;
- #define S3L_logTransform3D(t)\
- printf("Transform3D: T = [%d %d %d], R = [%d %d %d], S = [%d %d %d]\n",\
- (t).translation.x,(t).translation.y,(t).translation.z,\
- (t).rotation.x,(t).rotation.y,(t).rotation.z,\
- (t).scale.x,(t).scale.y,(t).scale.z)
- static inline void S3L_initTransform3D(S3L_Transform3D *t);
- void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t);
- void S3L_setTransform3D(
- S3L_Unit tx,
- S3L_Unit ty,
- S3L_Unit tz,
- S3L_Unit rx,
- S3L_Unit ry,
- S3L_Unit rz,
- S3L_Unit sx,
- S3L_Unit sy,
- S3L_Unit sz,
- S3L_Transform3D *t);
- /** Converts rotation transformation to three direction vectors of given length
- (any one can be NULL, in which case it won't be computed). */
- void S3L_rotationToDirections(
- S3L_Vec4 rotation,
- S3L_Unit length,
- S3L_Vec4 *forw,
- S3L_Vec4 *right,
- S3L_Vec4 *up);
- /** 4x4 matrix, used mostly for 3D transforms. The indexing is this:
- matrix[column][row]. */
- typedef S3L_Unit S3L_Mat4[4][4];
- #define S3L_logMat4(m)\
- printf("Mat4:\n %d %d %d %d\n %d %d %d %d\n %d %d %d %d\n %d %d %d %d\n"\
- ,(m)[0][0],(m)[1][0],(m)[2][0],(m)[3][0],\
- (m)[0][1],(m)[1][1],(m)[2][1],(m)[3][1],\
- (m)[0][2],(m)[1][2],(m)[2][2],(m)[3][2],\
- (m)[0][3],(m)[1][3],(m)[2][3],(m)[3][3])
- /** Initializes a 4x4 matrix to identity. */
- static inline void S3L_initMat4(S3L_Mat4 *m);
- void S3L_transposeMat4(S3L_Mat4 *m);
- void S3L_makeTranslationMat(
- S3L_Unit offsetX,
- S3L_Unit offsetY,
- S3L_Unit offsetZ,
- S3L_Mat4 *m);
- /** Makes a scaling matrix. DON'T FORGET: scale of 1.0 is set with
- S3L_FRACTIONS_PER_UNIT! */
- void S3L_makeScaleMatrix(
- S3L_Unit scaleX,
- S3L_Unit scaleY,
- S3L_Unit scaleZ,
- S3L_Mat4 *m);
- /** Makes a matrix for rotation in the ZXY order. */
- void S3L_makeRotationMatrixZXY(
- S3L_Unit byX,
- S3L_Unit byY,
- S3L_Unit byZ,
- S3L_Mat4 *m);
- void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 *m);
- void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 *m);
- /** Multiplies a vector by a matrix with normalization by
- S3L_FRACTIONS_PER_UNIT. Result is stored in the input vector. */
- void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 *m);
- /** Same as S3L_vec4Xmat4 but faster, because this version doesn't compute the
- W component of the result, which is usually not needed. */
- void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 *m);
- /** Multiplies two matrices with normalization by S3L_FRACTIONS_PER_UNIT.
- Result is stored in the first matrix. The result represents a transformation
- that has the same effect as applying the transformation represented by m1 and
- then m2 (in that order). */
- void S3L_mat4Xmat4(S3L_Mat4 *m1, S3L_Mat4 *m2);
- typedef struct
- {
- S3L_Unit focalLength; ///< Defines the field of view (FOV).
- S3L_Transform3D transform;
- } S3L_Camera;
- void S3L_initCamera(S3L_Camera *camera);
- typedef struct
- {
- uint8_t backfaceCulling; /**< What backface culling to use. Possible
- values:
- - 0 none
- - 1 clock-wise
- - 2 counter clock-wise */
- int8_t visible; /**< Can be used to easily hide the model. */
- } S3L_DrawConfig;
- void S3L_initDrawConfig(S3L_DrawConfig *config);
- typedef struct
- {
- const S3L_Unit *vertices;
- S3L_Index vertexCount;
- const S3L_Index *triangles;
- S3L_Index triangleCount;
- S3L_Transform3D transform;
- S3L_Mat4 *customTransformMatrix; /**< This can be used to override the
- transform (if != 0) with a custom
- transform matrix, which is more
- general. */
- S3L_DrawConfig config;
- } S3L_Model3D; ///< Represents a 3D model.
- void S3L_initModel3D(
- const S3L_Unit *vertices,
- S3L_Unit vertexCount,
- const S3L_Index *triangles,
- S3L_Index triangleCount,
- S3L_Model3D *model);
- typedef struct
- {
- S3L_Model3D *models;
- S3L_Index modelCount;
- S3L_Camera camera;
- } S3L_Scene; ///< Represent the 3D scene to be rendered.
- void S3L_initScene(
- S3L_Model3D *models,
- S3L_Index modelCount,
- S3L_Scene *scene);
- typedef struct
- {
- S3L_ScreenCoord x; ///< Screen X coordinate.
- S3L_ScreenCoord y; ///< Screen Y coordinate.
- S3L_Unit barycentric[3]; /**< Barycentric coords correspond to the three
- vertices. These serve to locate the pixel on a
- triangle and interpolate values between it's
- three points. Each one goes from 0 to
- S3L_FRACTIONS_PER_UNIT (including), but due to
- rounding error may fall outside this range (you
- can use S3L_correctBarycentricCoords to fix this
- for the price of some performance). The sum of
- the three coordinates will always be exactly
- S3L_FRACTIONS_PER_UNIT. */
- S3L_Index modelIndex; ///< Model index within the scene.
- S3L_Index triangleIndex; ///< Triangle index within the model.
- uint32_t triangleID; /**< Unique ID of the triangle withing the whole
- scene. This can be used e.g. by a cache to
- quickly find out if a triangle has changed. */
- S3L_Unit depth; ///< Depth (only if depth is turned on).
- S3L_Unit previousZ; /**< Z-buffer value (not necessarily world depth in
- S3L_Units!) that was in the z-buffer on the
- pixels position before this pixel was
- rasterized. This can be used to set the value
- back, e.g. for transparency. */
- S3L_ScreenCoord triangleSize[2]; /**< Rasterized triangle width and height,
- can be used e.g. for MIP mapping. */
- } S3L_PixelInfo; /**< Used to pass the info about a rasterized pixel
- (fragment) to the user-defined drawing func. */
- static inline void S3L_initPixelInfo(S3L_PixelInfo *p);
- /** Corrects barycentric coordinates so that they exactly meet the defined
- conditions (each fall into <0,S3L_FRACTIONS_PER_UNIT>, sum =
- S3L_FRACTIONS_PER_UNIT). Note that doing this per-pixel can slow the program
- down significantly. */
- static inline void S3L_correctBarycentricCoords(S3L_Unit barycentric[3]);
- // general helper functions
- static inline S3L_Unit S3L_abs(S3L_Unit value);
- static inline S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2);
- static inline S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2);
- static inline S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2);
- static inline S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod);
- static inline S3L_Unit S3L_nonZero(S3L_Unit value);
- static inline S3L_Unit S3L_zeroClamp(S3L_Unit value);
- S3L_Unit S3L_sin(S3L_Unit x);
- S3L_Unit S3L_asin(S3L_Unit x);
- static inline S3L_Unit S3L_cos(S3L_Unit x);
- S3L_Unit S3L_vec3Length(S3L_Vec4 v);
- S3L_Unit S3L_sqrt(S3L_Unit value);
- /** Projects a single point from 3D space to the screen space (pixels), which
- can be useful e.g. for drawing sprites. The w component of input and result
- holds the point size. If this size is 0 in the result, the sprite is outside
- the view. */
- void project3DPointToScreen(
- S3L_Vec4 point,
- S3L_Camera camera,
- S3L_Vec4 *result);
- /** Computes a normalized normal of given triangle. */
- void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2,
- S3L_Vec4 *n);
- /** Helper function for retrieving per-vertex indexed values from an array,
- e.g. texturing (UV) coordinates. The 'indices' array contains three indices
- for each triangle, each index pointing into 'values' array, which contains
- the values, each one consisting of 'numComponents' components (e.g. 2 for
- UV coordinates). The three values are retrieved into 'v0', 'v1' and 'v2'
- vectors (into x, y, z and w, depending on 'numComponents'). This function is
- meant to be used per-triangle (typically from a cache), NOT per-pixel, as it
- is not as fast as possible! */
- void S3L_getIndexedTriangleValues(
- S3L_Index triangleIndex,
- const S3L_Index *indices,
- const S3L_Unit *values,
- uint8_t numComponents,
- S3L_Vec4 *v0,
- S3L_Vec4 *v1,
- S3L_Vec4 *v2);
- /** Computes a normalized normal for every vertex of given model (this is
- relatively slow and SHOUDN'T be done each frame). The dst array must have a
- sufficient size preallocated! The size is: number of model vertices * 3 *
- sizeof(S3L_Unit). Note that for advanced allowing sharp edges it is not
- sufficient to have per-vertex normals, but must be per-triangle. This
- function doesn't support this.
- The function computes a normal for each vertex by averaging normals of
- the triangles containing the vertex. The maximum number of these triangle
- normals that will be averaged is set with
- S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE. */
- void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst,
- int8_t transformNormals);
- /** Interpolated between two values, v1 and v2, in the same ratio as t is to
- tMax. Does NOT prevent zero division. */
- static inline S3L_Unit S3L_interpolate(
- S3L_Unit v1,
- S3L_Unit v2,
- S3L_Unit t,
- S3L_Unit tMax);
- /** Same as S3L_interpolate but with v1 == 0. Should be faster. */
- static inline S3L_Unit S3L_interpolateFrom0(
- S3L_Unit v2,
- S3L_Unit t,
- S3L_Unit tMax);
- /** Like S3L_interpolate, but uses a parameter that goes from 0 to
- S3L_FRACTIONS_PER_UNIT - 1, which can be faster. */
- static inline S3L_Unit S3L_interpolateByUnit(
- S3L_Unit v1,
- S3L_Unit v2,
- S3L_Unit t);
- /** Same as S3L_interpolateByUnit but with v1 == 0. Should be faster. */
- static inline S3L_Unit S3L_interpolateByUnitFrom0(
- S3L_Unit v2,
- S3L_Unit t);
- static inline S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b);
- /** Returns a value interpolated between the three triangle vertices based on
- barycentric coordinates. */
- static inline S3L_Unit S3L_interpolateBarycentric(
- S3L_Unit value0,
- S3L_Unit value1,
- S3L_Unit value2,
- S3L_Unit barycentric[3]);
- static inline void S3L_mapProjectionPlaneToScreen(
- S3L_Vec4 point,
- S3L_ScreenCoord *screenX,
- S3L_ScreenCoord *screenY);
- /** Draws a triangle according to given config. The vertices are specified in
- Screen Space space (pixels). If perspective correction is enabled, each
- vertex has to have a depth (Z position in camera space) specified in the Z
- component. */
- void S3L_drawTriangle(
- S3L_Vec4 point0,
- S3L_Vec4 point1,
- S3L_Vec4 point2,
- S3L_Index modelIndex,
- S3L_Index triangleIndex);
- /** This should be called before rendering each frame. The function clears
- buffers and does potentially other things needed for the frame. */
- void S3L_newFrame(void);
- void S3L_zBufferClear(void);
- void S3L_stencilBufferClear(void);
- /** Writes a value (not necessarily depth! depends on the format of z-buffer)
- to z-buffer (if enabled). Does NOT check boundaries! */
- void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value);
- /** Reads a value (not necessarily depth! depends on the format of z-buffer)
- from z-buffer (if enabled). Does NOT check boundaries! */
- S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y);
- static inline void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle);
- /** Predefined vertices of a cube to simply insert in an array. These come with
- S3L_CUBE_TRIANGLES and S3L_CUBE_TEXCOORDS. */
- #define S3L_CUBE_VERTICES(m)\
- /* 0 front, bottom, right */\
- m/2, -m/2, -m/2,\
- /* 1 front, bottom, left */\
- -m/2, -m/2, -m/2,\
- /* 2 front, top, right */\
- m/2, m/2, -m/2,\
- /* 3 front, top, left */\
- -m/2, m/2, -m/2,\
- /* 4 back, bottom, right */\
- m/2, -m/2, m/2,\
- /* 5 back, bottom, left */\
- -m/2, -m/2, m/2,\
- /* 6 back, top, right */\
- m/2, m/2, m/2,\
- /* 7 back, top, left */\
- -m/2, m/2, m/2
- #define S3L_CUBE_VERTEX_COUNT 8
- /** Predefined triangle indices of a cube, to be used with S3L_CUBE_VERTICES
- and S3L_CUBE_TEXCOORDS. */
- #define S3L_CUBE_TRIANGLES\
- 3, 0, 2, /* front */\
- 1, 0, 3,\
- 0, 4, 2, /* right */\
- 2, 4, 6,\
- 4, 5, 6, /* back */\
- 7, 6, 5,\
- 3, 7, 1, /* left */\
- 1, 7, 5,\
- 6, 3, 2, /* top */\
- 7, 3, 6,\
- 1, 4, 0, /* bottom */\
- 5, 4, 1
- #define S3L_CUBE_TRIANGLE_COUNT 12
- /** Predefined texture coordinates of a cube, corresponding to triangles (NOT
- vertices), to be used with S3L_CUBE_VERTICES and S3L_CUBE_TRIANGLES. */
- #define S3L_CUBE_TEXCOORDS(m)\
- 0,0, m,m, m,0,\
- 0,m, m,m, 0,0,\
- m,m, m,0, 0,m,\
- 0,m, m,0, 0,0,\
- m,0, 0,0, m,m,\
- 0,m, m,m, 0,0,\
- 0,0, 0,m, m,0,\
- m,0, 0,m, m,m,\
- 0,0, m,m, m,0,\
- 0,m, m,m, 0,0,\
- m,0, 0,m, m,m,\
- 0,0, 0,m, m,0
- //=============================================================================
- // privates
- #define S3L_UNUSED(what) (void)(what) ///< helper macro for unused vars
- #define S3L_HALF_RESOLUTION_X (S3L_RESOLUTION_X >> 1)
- #define S3L_HALF_RESOLUTION_Y (S3L_RESOLUTION_Y >> 1)
- #define S3L_PROJECTION_PLANE_HEIGHT\
- ((S3L_RESOLUTION_Y * S3L_FRACTIONS_PER_UNIT * 2) / S3L_RESOLUTION_X)
- #if S3L_Z_BUFFER == 1
- #define S3L_MAX_DEPTH 2147483647
- S3L_Unit S3L_zBuffer[S3L_MAX_PIXELS];
- #define S3L_zBufferFormat(depth) (depth)
- #elif S3L_Z_BUFFER == 2
- #define S3L_MAX_DEPTH 255
- uint8_t S3L_zBuffer[S3L_MAX_PIXELS];
- #define S3L_zBufferFormat(depth)\
- S3L_min(255,(depth) >> S3L_REDUCED_Z_BUFFER_GRANULARITY)
- #endif
- #if S3L_Z_BUFFER
- static inline int8_t S3L_zTest(
- S3L_ScreenCoord x,
- S3L_ScreenCoord y,
- S3L_Unit depth)
- {
- uint32_t index = y * S3L_RESOLUTION_X + x;
- depth = S3L_zBufferFormat(depth);
- #if S3L_Z_BUFFER == 2
- #define cmp <= /* For reduced z-buffer we need equality test, because
- otherwise pixels at the maximum depth (255) would never be
- drawn over the background (which also has the depth of
- 255). */
- #else
- #define cmp < /* For normal z-buffer we leave out equality test to not waste
- time by drawing over already drawn pixls. */
- #endif
- if (depth cmp S3L_zBuffer[index])
- {
- S3L_zBuffer[index] = depth;
- return 1;
- }
- #undef cmp
- return 0;
- }
- #endif
- S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y)
- {
- #if S3L_Z_BUFFER
- return S3L_zBuffer[y * S3L_RESOLUTION_X + x];
- #else
- S3L_UNUSED(x);
- S3L_UNUSED(y);
- return 0;
- #endif
- }
- void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value)
- {
- #if S3L_Z_BUFFER
- S3L_zBuffer[y * S3L_RESOLUTION_X + x] = value;
- #else
- S3L_UNUSED(x);
- S3L_UNUSED(y);
- S3L_UNUSED(value);
- #endif
- }
- #if S3L_STENCIL_BUFFER
- #define S3L_STENCIL_BUFFER_SIZE\
- ((S3L_RESOLUTION_X * S3L_RESOLUTION_Y - 1) / 8 + 1)
- uint8_t S3L_stencilBuffer[S3L_STENCIL_BUFFER_SIZE];
- static inline int8_t S3L_stencilTest(
- S3L_ScreenCoord x,
- S3L_ScreenCoord y)
- {
- uint32_t index = y * S3L_RESOLUTION_X + x;
- uint32_t bit = (index & 0x00000007);
- index = index >> 3;
- uint8_t val = S3L_stencilBuffer[index];
- if ((val >> bit) & 0x1)
- return 0;
-
- S3L_stencilBuffer[index] = val | (0x1 << bit);
- return 1;
- }
- #endif
- #define S3L_COMPUTE_LERP_DEPTH\
- (S3L_COMPUTE_DEPTH && (S3L_PERSPECTIVE_CORRECTION == 0))
- #define S3L_SIN_TABLE_LENGTH 128
- static const S3L_Unit S3L_sinTable[S3L_SIN_TABLE_LENGTH] =
- {
- /* 511 was chosen here as a highest number that doesn't overflow during
- compilation for S3L_FRACTIONS_PER_UNIT == 1024 */
- (0*S3L_FRACTIONS_PER_UNIT)/511, (6*S3L_FRACTIONS_PER_UNIT)/511,
- (12*S3L_FRACTIONS_PER_UNIT)/511, (18*S3L_FRACTIONS_PER_UNIT)/511,
- (25*S3L_FRACTIONS_PER_UNIT)/511, (31*S3L_FRACTIONS_PER_UNIT)/511,
- (37*S3L_FRACTIONS_PER_UNIT)/511, (43*S3L_FRACTIONS_PER_UNIT)/511,
- (50*S3L_FRACTIONS_PER_UNIT)/511, (56*S3L_FRACTIONS_PER_UNIT)/511,
- (62*S3L_FRACTIONS_PER_UNIT)/511, (68*S3L_FRACTIONS_PER_UNIT)/511,
- (74*S3L_FRACTIONS_PER_UNIT)/511, (81*S3L_FRACTIONS_PER_UNIT)/511,
- (87*S3L_FRACTIONS_PER_UNIT)/511, (93*S3L_FRACTIONS_PER_UNIT)/511,
- (99*S3L_FRACTIONS_PER_UNIT)/511, (105*S3L_FRACTIONS_PER_UNIT)/511,
- (111*S3L_FRACTIONS_PER_UNIT)/511, (118*S3L_FRACTIONS_PER_UNIT)/511,
- (124*S3L_FRACTIONS_PER_UNIT)/511, (130*S3L_FRACTIONS_PER_UNIT)/511,
- (136*S3L_FRACTIONS_PER_UNIT)/511, (142*S3L_FRACTIONS_PER_UNIT)/511,
- (148*S3L_FRACTIONS_PER_UNIT)/511, (154*S3L_FRACTIONS_PER_UNIT)/511,
- (160*S3L_FRACTIONS_PER_UNIT)/511, (166*S3L_FRACTIONS_PER_UNIT)/511,
- (172*S3L_FRACTIONS_PER_UNIT)/511, (178*S3L_FRACTIONS_PER_UNIT)/511,
- (183*S3L_FRACTIONS_PER_UNIT)/511, (189*S3L_FRACTIONS_PER_UNIT)/511,
- (195*S3L_FRACTIONS_PER_UNIT)/511, (201*S3L_FRACTIONS_PER_UNIT)/511,
- (207*S3L_FRACTIONS_PER_UNIT)/511, (212*S3L_FRACTIONS_PER_UNIT)/511,
- (218*S3L_FRACTIONS_PER_UNIT)/511, (224*S3L_FRACTIONS_PER_UNIT)/511,
- (229*S3L_FRACTIONS_PER_UNIT)/511, (235*S3L_FRACTIONS_PER_UNIT)/511,
- (240*S3L_FRACTIONS_PER_UNIT)/511, (246*S3L_FRACTIONS_PER_UNIT)/511,
- (251*S3L_FRACTIONS_PER_UNIT)/511, (257*S3L_FRACTIONS_PER_UNIT)/511,
- (262*S3L_FRACTIONS_PER_UNIT)/511, (268*S3L_FRACTIONS_PER_UNIT)/511,
- (273*S3L_FRACTIONS_PER_UNIT)/511, (278*S3L_FRACTIONS_PER_UNIT)/511,
- (283*S3L_FRACTIONS_PER_UNIT)/511, (289*S3L_FRACTIONS_PER_UNIT)/511,
- (294*S3L_FRACTIONS_PER_UNIT)/511, (299*S3L_FRACTIONS_PER_UNIT)/511,
- (304*S3L_FRACTIONS_PER_UNIT)/511, (309*S3L_FRACTIONS_PER_UNIT)/511,
- (314*S3L_FRACTIONS_PER_UNIT)/511, (319*S3L_FRACTIONS_PER_UNIT)/511,
- (324*S3L_FRACTIONS_PER_UNIT)/511, (328*S3L_FRACTIONS_PER_UNIT)/511,
- (333*S3L_FRACTIONS_PER_UNIT)/511, (338*S3L_FRACTIONS_PER_UNIT)/511,
- (343*S3L_FRACTIONS_PER_UNIT)/511, (347*S3L_FRACTIONS_PER_UNIT)/511,
- (352*S3L_FRACTIONS_PER_UNIT)/511, (356*S3L_FRACTIONS_PER_UNIT)/511,
- (361*S3L_FRACTIONS_PER_UNIT)/511, (365*S3L_FRACTIONS_PER_UNIT)/511,
- (370*S3L_FRACTIONS_PER_UNIT)/511, (374*S3L_FRACTIONS_PER_UNIT)/511,
- (378*S3L_FRACTIONS_PER_UNIT)/511, (382*S3L_FRACTIONS_PER_UNIT)/511,
- (386*S3L_FRACTIONS_PER_UNIT)/511, (391*S3L_FRACTIONS_PER_UNIT)/511,
- (395*S3L_FRACTIONS_PER_UNIT)/511, (398*S3L_FRACTIONS_PER_UNIT)/511,
- (402*S3L_FRACTIONS_PER_UNIT)/511, (406*S3L_FRACTIONS_PER_UNIT)/511,
- (410*S3L_FRACTIONS_PER_UNIT)/511, (414*S3L_FRACTIONS_PER_UNIT)/511,
- (417*S3L_FRACTIONS_PER_UNIT)/511, (421*S3L_FRACTIONS_PER_UNIT)/511,
- (424*S3L_FRACTIONS_PER_UNIT)/511, (428*S3L_FRACTIONS_PER_UNIT)/511,
- (431*S3L_FRACTIONS_PER_UNIT)/511, (435*S3L_FRACTIONS_PER_UNIT)/511,
- (438*S3L_FRACTIONS_PER_UNIT)/511, (441*S3L_FRACTIONS_PER_UNIT)/511,
- (444*S3L_FRACTIONS_PER_UNIT)/511, (447*S3L_FRACTIONS_PER_UNIT)/511,
- (450*S3L_FRACTIONS_PER_UNIT)/511, (453*S3L_FRACTIONS_PER_UNIT)/511,
- (456*S3L_FRACTIONS_PER_UNIT)/511, (459*S3L_FRACTIONS_PER_UNIT)/511,
- (461*S3L_FRACTIONS_PER_UNIT)/511, (464*S3L_FRACTIONS_PER_UNIT)/511,
- (467*S3L_FRACTIONS_PER_UNIT)/511, (469*S3L_FRACTIONS_PER_UNIT)/511,
- (472*S3L_FRACTIONS_PER_UNIT)/511, (474*S3L_FRACTIONS_PER_UNIT)/511,
- (476*S3L_FRACTIONS_PER_UNIT)/511, (478*S3L_FRACTIONS_PER_UNIT)/511,
- (481*S3L_FRACTIONS_PER_UNIT)/511, (483*S3L_FRACTIONS_PER_UNIT)/511,
- (485*S3L_FRACTIONS_PER_UNIT)/511, (487*S3L_FRACTIONS_PER_UNIT)/511,
- (488*S3L_FRACTIONS_PER_UNIT)/511, (490*S3L_FRACTIONS_PER_UNIT)/511,
- (492*S3L_FRACTIONS_PER_UNIT)/511, (494*S3L_FRACTIONS_PER_UNIT)/511,
- (495*S3L_FRACTIONS_PER_UNIT)/511, (497*S3L_FRACTIONS_PER_UNIT)/511,
- (498*S3L_FRACTIONS_PER_UNIT)/511, (499*S3L_FRACTIONS_PER_UNIT)/511,
- (501*S3L_FRACTIONS_PER_UNIT)/511, (502*S3L_FRACTIONS_PER_UNIT)/511,
- (503*S3L_FRACTIONS_PER_UNIT)/511, (504*S3L_FRACTIONS_PER_UNIT)/511,
- (505*S3L_FRACTIONS_PER_UNIT)/511, (506*S3L_FRACTIONS_PER_UNIT)/511,
- (507*S3L_FRACTIONS_PER_UNIT)/511, (507*S3L_FRACTIONS_PER_UNIT)/511,
- (508*S3L_FRACTIONS_PER_UNIT)/511, (509*S3L_FRACTIONS_PER_UNIT)/511,
- (509*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511,
- (510*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511,
- (510*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511
- };
- #define S3L_SIN_TABLE_UNIT_STEP\
- (S3L_FRACTIONS_PER_UNIT / (S3L_SIN_TABLE_LENGTH * 4))
- void S3L_initVec4(S3L_Vec4 *v)
- {
- v->x = 0; v->y = 0; v->z = 0; v->w = S3L_FRACTIONS_PER_UNIT;
- }
- void S3L_setVec4(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y, S3L_Unit z, S3L_Unit w)
- {
- v->x = x;
- v->y = y;
- v->z = z;
- v->w = w;
- }
- void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added)
- {
- result->x += added.x;
- result->y += added.y;
- result->z += added.z;
- }
- void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted)
- {
- result->x -= substracted.x;
- result->y -= substracted.y;
- result->z -= substracted.z;
- }
- void S3L_initMat4(S3L_Mat4 *m)
- {
- #define M(x,y) (*m)[x][y]
- #define S S3L_FRACTIONS_PER_UNIT
- M(0,0) = S; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0;
- M(0,1) = 0; M(1,1) = S; M(2,1) = 0; M(3,1) = 0;
- M(0,2) = 0; M(1,2) = 0; M(2,2) = S; M(3,2) = 0;
- M(0,3) = 0; M(1,3) = 0; M(2,3) = 0; M(3,3) = S;
- #undef M
- #undef S
- }
- S3L_Unit S3L_dotProductVec3(S3L_Vec4 a, S3L_Vec4 b)
- {
- return (a.x * b.x + a.y * b.y + a.z * b.z) / S3L_FRACTIONS_PER_UNIT;
- }
- void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result)
- {
- S3L_Unit d = 2 * S3L_dotProductVec3(toLight,normal);
- result->x = (normal.x * d) / S3L_FRACTIONS_PER_UNIT - toLight.x;
- result->y = (normal.y * d) / S3L_FRACTIONS_PER_UNIT - toLight.y;
- result->z = (normal.z * d) / S3L_FRACTIONS_PER_UNIT - toLight.z;
- }
- void S3L_crossProduct(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result)
- {
- result->x = a.y * b.z - a.z * b.y;
- result->y = a.z * b.x - a.x * b.z;
- result->z = a.x * b.y - a.y * b.x;
- }
- void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2, S3L_Vec4 *n)
- {
- #define ANTI_OVERFLOW 32
- t1.x = (t1.x - t0.x) / ANTI_OVERFLOW;
- t1.y = (t1.y - t0.y) / ANTI_OVERFLOW;
- t1.z = (t1.z - t0.z) / ANTI_OVERFLOW;
- t2.x = (t2.x - t0.x) / ANTI_OVERFLOW;
- t2.y = (t2.y - t0.y) / ANTI_OVERFLOW;
- t2.z = (t2.z - t0.z) / ANTI_OVERFLOW;
- #undef ANTI_OVERFLOW
- S3L_crossProduct(t1,t2,n);
- S3L_normalizeVec3(n);
- }
- void S3L_getIndexedTriangleValues(
- S3L_Index triangleIndex,
- const S3L_Index *indices,
- const S3L_Unit *values,
- uint8_t numComponents,
- S3L_Vec4 *v0,
- S3L_Vec4 *v1,
- S3L_Vec4 *v2)
- {
- uint32_t i0, i1;
- S3L_Unit *value;
- i0 = triangleIndex * 3;
- i1 = indices[i0] * numComponents;
- value = (S3L_Unit *) v0;
- if (numComponents > 4)
- numComponents = 4;
- for (uint8_t j = 0; j < numComponents; ++j)
- {
- *value = values[i1];
- i1++;
- value++;
- }
- i0++;
- i1 = indices[i0] * numComponents;
- value = (S3L_Unit *) v1;
- for (uint8_t j = 0; j < numComponents; ++j)
- {
- *value = values[i1];
- i1++;
- value++;
- }
- i0++;
- i1 = indices[i0] * numComponents;
- value = (S3L_Unit *) v2;
- for (uint8_t j = 0; j < numComponents; ++j)
- {
- *value = values[i1];
- i1++;
- value++;
- }
- }
- void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst,
- int8_t transformNormals)
- {
- S3L_Index vPos = 0;
- S3L_Vec4 n;
- n.w = 0;
- S3L_Vec4 ns[S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE];
- S3L_Index normalCount;
- for (uint32_t i = 0; i < model.vertexCount; ++i)
- {
- normalCount = 0;
- for (uint32_t j = 0; j < model.triangleCount * 3; j += 3)
- {
- if (
- (model.triangles[j] == i) ||
- (model.triangles[j + 1] == i) ||
- (model.triangles[j + 2] == i))
- {
- S3L_Vec4 t0, t1, t2;
- uint32_t vIndex;
- #define getVertex(n)\
- vIndex = model.triangles[j + n] * 3;\
- t##n.x = model.vertices[vIndex];\
- vIndex++;\
- t##n.y = model.vertices[vIndex];\
- vIndex++;\
- t##n.z = model.vertices[vIndex];
- getVertex(0)
- getVertex(1)
- getVertex(2)
- #undef getVertex
-
- S3L_triangleNormal(t0,t1,t2,&(ns[normalCount]));
- normalCount++;
- if (normalCount >= S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE)
- break;
- }
- }
-
- n.x = S3L_FRACTIONS_PER_UNIT;
- n.y = 0;
- n.z = 0;
- if (normalCount != 0)
- {
- // compute average
- n.x = 0;
- for (uint8_t i = 0; i < normalCount; ++i)
- {
- n.x += ns[i].x;
- n.y += ns[i].y;
- n.z += ns[i].z;
- }
- n.x /= normalCount;
- n.y /= normalCount;
- n.z /= normalCount;
- S3L_normalizeVec3(&n);
- }
- dst[vPos] = n.x;
- vPos++;
- dst[vPos] = n.y;
- vPos++;
- dst[vPos] = n.z;
- vPos++;
- }
-
- S3L_Mat4 m;
- S3L_makeWorldMatrix(model.transform,&m);
- if (transformNormals)
- for (S3L_Index i = 0; i < model.vertexCount * 3; i += 3)
- {
- n.x = dst[i];
- n.y = dst[i + 1];
- n.z = dst[i + 2];
- S3L_vec4Xmat4(&n,&m);
- dst[i] = n.x;
- dst[i + 1] = n.y;
- dst[i + 2] = n.z;
- }
- }
- void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 *m)
- {
- S3L_Vec4 vBackup;
- vBackup.x = v->x;
- vBackup.y = v->y;
- vBackup.z = v->z;
- vBackup.w = v->w;
- #define dotCol(col)\
- ((vBackup.x * (*m)[col][0]) +\
- (vBackup.y * (*m)[col][1]) +\
- (vBackup.z * (*m)[col][2]) +\
- (vBackup.w * (*m)[col][3])) / S3L_FRACTIONS_PER_UNIT
- v->x = dotCol(0);
- v->y = dotCol(1);
- v->z = dotCol(2);
- v->w = dotCol(3);
- }
- void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 *m)
- {
- S3L_Vec4 vBackup;
- #undef dotCol
- #define dotCol(col)\
- (vBackup.x * (*m)[col][0]) / S3L_FRACTIONS_PER_UNIT +\
- (vBackup.y * (*m)[col][1]) / S3L_FRACTIONS_PER_UNIT +\
- (vBackup.z * (*m)[col][2]) / S3L_FRACTIONS_PER_UNIT +\
- (*m)[col][3]
- vBackup.x = v->x;
- vBackup.y = v->y;
- vBackup.z = v->z;
- vBackup.w = v->w;
- v->x = dotCol(0);
- v->y = dotCol(1);
- v->z = dotCol(2);
- v->w = S3L_FRACTIONS_PER_UNIT;
- }
- #undef dotCol
- S3L_Unit S3L_abs(S3L_Unit value)
- {
- return value * (((value >= 0) << 1) - 1);
- }
- S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2)
- {
- return v1 >= v2 ? v2 : v1;
- }
- S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2)
- {
- return v1 >= v2 ? v1 : v2;
- }
- S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2)
- {
- return v >= v1 ? (v <= v2 ? v : v2) : v1;
- }
- S3L_Unit S3L_zeroClamp(S3L_Unit value)
- {
- return (value * (value >= 0));
- }
- S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod)
- {
- return value >= 0 ? (value % mod) : (mod + (value % mod) - 1);
- }
- S3L_Unit S3L_nonZero(S3L_Unit value)
- {
- return (value + (value == 0));
- }
- S3L_Unit S3L_interpolate(S3L_Unit v1, S3L_Unit v2, S3L_Unit t, S3L_Unit tMax)
- {
- return v1 + ((v2 - v1) * t) / tMax;
- }
- S3L_Unit S3L_interpolateByUnit(S3L_Unit v1, S3L_Unit v2, S3L_Unit t)
- {
- return v1 + ((v2 - v1) * t) / S3L_FRACTIONS_PER_UNIT;
- }
- S3L_Unit S3L_interpolateByUnitFrom0(S3L_Unit v2, S3L_Unit t)
- {
- return (v2 * t) / S3L_FRACTIONS_PER_UNIT;
- }
- S3L_Unit S3L_interpolateFrom0(S3L_Unit v2, S3L_Unit t, S3L_Unit tMax)
- {
- return (v2 * t) / tMax;
- }
- S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b)
- {
- return
- S3L_abs(a.x - b.x) +
- S3L_abs(a.y - b.y) +
- S3L_abs(a.z - b.z);
- }
- void S3L_mat4Xmat4(S3L_Mat4 *m1, S3L_Mat4 *m2)
- {
- S3L_Mat4 mat1;
- for (uint16_t row = 0; row < 4; ++row)
- for (uint16_t col = 0; col < 4; ++col)
- mat1[col][row] = (*m1)[col][row];
- for (uint16_t row = 0; row < 4; ++row)
- for (uint16_t col = 0; col < 4; ++col)
- {
- (*m1)[col][row] = 0;
- for (uint16_t i = 0; i < 4; ++i)
- (*m1)[col][row] +=
- (mat1[i][row] * (*m2)[col][i]) / S3L_FRACTIONS_PER_UNIT;
- }
- }
- S3L_Unit S3L_sin(S3L_Unit x)
- {
- x = S3L_wrap(x / S3L_SIN_TABLE_UNIT_STEP,S3L_SIN_TABLE_LENGTH * 4);
- int8_t positive = 1;
- if (x < S3L_SIN_TABLE_LENGTH)
- {
- }
- else if (x < S3L_SIN_TABLE_LENGTH * 2)
- {
- x = S3L_SIN_TABLE_LENGTH * 2 - x - 1;
- }
- else if (x < S3L_SIN_TABLE_LENGTH * 3)
- {
- x = x - S3L_SIN_TABLE_LENGTH * 2;
- positive = 0;
- }
- else
- {
- x = S3L_SIN_TABLE_LENGTH - (x - S3L_SIN_TABLE_LENGTH * 3) - 1;
- positive = 0;
- }
- return positive ? S3L_sinTable[x] : -1 * S3L_sinTable[x];
- }
- S3L_Unit S3L_asin(S3L_Unit x)
- {
- x = S3L_clamp(x,-S3L_FRACTIONS_PER_UNIT,S3L_FRACTIONS_PER_UNIT);
- int8_t sign = 1;
- if (x < 0)
- {
- sign = -1;
- x *= -1;
- }
- int16_t low = 0;
- int16_t high = S3L_SIN_TABLE_LENGTH -1;
- int16_t middle;
- while (low <= high) // binary search
- {
- middle = (low + high) / 2;
- S3L_Unit v = S3L_sinTable[middle];
- if (v > x)
- high = middle - 1;
- else if (v < x)
- low = middle + 1;
- else
- break;
- }
- middle *= S3L_SIN_TABLE_UNIT_STEP;
- return sign * middle;
- }
- S3L_Unit S3L_cos(S3L_Unit x)
- {
- return S3L_sin(x + S3L_FRACTIONS_PER_UNIT / 4);
- }
- void S3L_correctBarycentricCoords(S3L_Unit barycentric[3])
- {
- barycentric[0] = S3L_clamp(barycentric[0],0,S3L_FRACTIONS_PER_UNIT);
- barycentric[1] = S3L_clamp(barycentric[1],0,S3L_FRACTIONS_PER_UNIT);
- S3L_Unit d = S3L_FRACTIONS_PER_UNIT - barycentric[0] - barycentric[1];
- if (d < 0)
- {
- barycentric[0] += d;
- barycentric[2] = 0;
- }
- else
- barycentric[2] = d;
- }
- void S3L_makeTranslationMat(
- S3L_Unit offsetX,
- S3L_Unit offsetY,
- S3L_Unit offsetZ,
- S3L_Mat4 *m)
- {
- #define M(x,y) (*m)[x][y]
- #define S S3L_FRACTIONS_PER_UNIT
- M(0,0) = S; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0;
- M(0,1) = 0; M(1,1) = S; M(2,1) = 0; M(3,1) = 0;
- M(0,2) = 0; M(1,2) = 0; M(2,2) = S; M(3,2) = 0;
- M(0,3) = offsetX; M(1,3) = offsetY; M(2,3) = offsetZ; M(3,3) = S;
- #undef M
- #undef S
- }
- void S3L_makeScaleMatrix(
- S3L_Unit scaleX,
- S3L_Unit scaleY,
- S3L_Unit scaleZ,
- S3L_Mat4 *m)
- {
- #define M(x,y) (*m)[x][y]
- M(0,0) = scaleX; M(1,0) = 0; M(2,0) = 0; M(3,0) = 0;
- M(0,1) = 0; M(1,1) = scaleY; M(2,1) = 0; M(3,1) = 0;
- M(0,2) = 0; M(1,2) = 0; M(2,2) = scaleZ; M(3,2) = 0;
- M(0,3) = 0; M(1,3) = 0; M(2,3) = 0; M(3,3) = S3L_FRACTIONS_PER_UNIT;
- #undef M
- }
- void S3L_makeRotationMatrixZXY(
- S3L_Unit byX,
- S3L_Unit byY,
- S3L_Unit byZ,
- S3L_Mat4 *m)
- {
- byX *= -1;
- byY *= -1;
- byZ *= -1;
- S3L_Unit sx = S3L_sin(byX);
- S3L_Unit sy = S3L_sin(byY);
- S3L_Unit sz = S3L_sin(byZ);
- S3L_Unit cx = S3L_cos(byX);
- S3L_Unit cy = S3L_cos(byY);
- S3L_Unit cz = S3L_cos(byZ);
- #define M(x,y) (*m)[x][y]
- #define S S3L_FRACTIONS_PER_UNIT
- M(0,0) = (cy * cz) / S + (sy * sx * sz) / (S * S);
- M(1,0) = (cx * sz) / S;
- M(2,0) = (cy * sx * sz) / (S * S) - (cz * sy) / S;
- M(3,0) = 0;
- M(0,1) = (cz * sy * sx) / (S * S) - (cy * sz) / S;
- M(1,1) = (cx * cz) / S;
- M(2,1) = (cy * cz * sx) / (S * S) + (sy * sz) / S;
- M(3,1) = 0;
- M(0,2) = (cx * sy) / S;
- M(1,2) = -1 * sx;
- M(2,2) = (cy * cx) / S;
- M(3,2) = 0;
- M(0,3) = 0;
- M(1,3) = 0;
- M(2,3) = 0;
- M(3,3) = S3L_FRACTIONS_PER_UNIT;
- #undef M
- #undef S
- }
- S3L_Unit S3L_sqrt(S3L_Unit value)
- {
- int8_t sign = 1;
- if (value < 0)
- {
- sign = -1;
- value *= -1;
- }
- uint32_t result = 0;
- uint32_t a = value;
- uint32_t b = 1u << 30;
- while (b > a)
- b >>= 2;
- while (b != 0)
- {
- if (a >= result + b)
- {
- a -= result + b;
- result = result + 2 * b;
- }
- b >>= 2;
- result >>= 1;
- }
- return result * sign;
- }
- S3L_Unit S3L_vec3Length(S3L_Vec4 v)
- {
- return S3L_sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
- }
- S3L_Unit S3L_vec2Length(S3L_Vec4 v)
- {
- return S3L_sqrt(v.x * v.x + v.y * v.y);
- }
- void S3L_normalizeVec3(S3L_Vec4 *v)
- {
- #define SCALE 16
- #define BOTTOM_LIMIT 16
- #define UPPER_LIMIT 900
- /* Here we try to decide if the vector is too small and would cause
- inaccurate result due to very its inaccurate length. If so, we scale
- it up. We can't scale up everything as big vectors overflow in length
- calculations. */
- if (
- S3L_abs(v->x) <= BOTTOM_LIMIT &&
- S3L_abs(v->y) <= BOTTOM_LIMIT &&
- S3L_abs(v->z) <= BOTTOM_LIMIT)
- {
- v->x *= SCALE;
- v->y *= SCALE;
- v->z *= SCALE;
- }
- else if (
- S3L_abs(v->x) > UPPER_LIMIT ||
- S3L_abs(v->y) > UPPER_LIMIT ||
- S3L_abs(v->z) > UPPER_LIMIT)
- {
- v->x /= SCALE;
- v->y /= SCALE;
- v->z /= SCALE;
- }
-
- #undef SCALE
- #undef BOTTOM_LIMIT
- #undef UPPER_LIMIT
- S3L_Unit l = S3L_vec3Length(*v);
- if (l == 0)
- return;
- v->x = (v->x * S3L_FRACTIONS_PER_UNIT) / l;
- v->y = (v->y * S3L_FRACTIONS_PER_UNIT) / l;
- v->z = (v->z * S3L_FRACTIONS_PER_UNIT) / l;
- }
- void S3L_normalizeVec3Fast(S3L_Vec4 *v)
- {
- S3L_Unit l = S3L_vec3Length(*v);
- if (l == 0)
- return;
- v->x = (v->x * S3L_FRACTIONS_PER_UNIT) / l;
- v->y = (v->y * S3L_FRACTIONS_PER_UNIT) / l;
- v->z = (v->z * S3L_FRACTIONS_PER_UNIT) / l;
- }
- void S3L_initTransform3D(S3L_Transform3D *t)
- {
- S3L_initVec4(&(t->translation));
- S3L_initVec4(&(t->rotation));
- t->scale.x = S3L_FRACTIONS_PER_UNIT;
- t->scale.y = S3L_FRACTIONS_PER_UNIT;
- t->scale.z = S3L_FRACTIONS_PER_UNIT;
- t->scale.w = 0;
- }
- /** Performs perspecive division (z-divide). Does NOT check for division by
- zero. */
- static inline void S3L_perspectiveDivide(S3L_Vec4 *vector,
- S3L_Unit focalLength)
- {
- vector->x = (vector->x * focalLength) / vector->z;
- vector->y = (vector->y * focalLength) / vector->z;
- }
- void project3DPointToScreen(
- S3L_Vec4 point,
- S3L_Camera camera,
- S3L_Vec4 *result)
- {
- S3L_Mat4 m;
- S3L_makeCameraMatrix(camera.transform,&m);
- S3L_Unit s = point.w;
- point.w = S3L_FRACTIONS_PER_UNIT;
- S3L_vec3Xmat4(&point,&m);
- point.z = S3L_nonZero(point.z);
- S3L_perspectiveDivide(&point,camera.focalLength);
- S3L_ScreenCoord x, y;
- S3L_mapProjectionPlaneToScreen(point,&x,&y);
- result->x = x;
- result->y = y;
- result->z = point.z;
- result->w =
- (point.z <= 0) ? 0 :
- (
- (s * camera.focalLength * S3L_RESOLUTION_X) /
- (point.z * S3L_FRACTIONS_PER_UNIT)
- );
- }
- void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t)
- {
- S3L_Vec4 v;
- v.x = pointTo.x - t->translation.x;
- v.y = pointTo.z - t->translation.z;
- S3L_Unit dx = v.x;
- S3L_Unit l = S3L_vec2Length(v);
- dx = (v.x * S3L_FRACTIONS_PER_UNIT) / S3L_nonZero(l); // normalize
- t->rotation.y = -1 * S3L_asin(dx);
- if (v.y < 0)
- t->rotation.y = S3L_FRACTIONS_PER_UNIT / 2 - t->rotation.y;
- v.x = pointTo.y - t->translation.y;
- v.y = l;
-
- l = S3L_vec2Length(v);
-
- dx = (v.x * S3L_FRACTIONS_PER_UNIT) / S3L_nonZero(l);
- t->rotation.x = S3L_asin(dx);
- }
- void S3L_setTransform3D(
- S3L_Unit tx,
- S3L_Unit ty,
- S3L_Unit tz,
- S3L_Unit rx,
- S3L_Unit ry,
- S3L_Unit rz,
- S3L_Unit sx,
- S3L_Unit sy,
- S3L_Unit sz,
- S3L_Transform3D *t)
- {
- t->translation.x = tx;
- t->translation.y = ty;
- t->translation.z = tz;
- t->rotation.x = rx;
- t->rotation.y = ry;
- t->rotation.z = rz;
- t->scale.x = sx;
- t->scale.y = sy;
- t->scale.z = sz;
- }
- void S3L_initCamera(S3L_Camera *camera)
- {
- camera->focalLength = S3L_FRACTIONS_PER_UNIT;
- S3L_initTransform3D(&(camera->transform));
- }
- void S3L_rotationToDirections(
- S3L_Vec4 rotation,
- S3L_Unit length,
- S3L_Vec4 *forw,
- S3L_Vec4 *right,
- S3L_Vec4 *up)
- {
- S3L_Mat4 m;
- S3L_makeRotationMatrixZXY(rotation.x,rotation.y,rotation.z,&m);
- if (forw != 0)
- {
- forw->x = 0;
- forw->y = 0;
- forw->z = length;
- S3L_vec3Xmat4(forw,&m);
- }
- if (right != 0)
- {
- right->x = length;
- right->y = 0;
- right->z = 0;
- S3L_vec3Xmat4(right,&m);
- }
- if (up != 0)
- {
- up->x = 0;
- up->y = length;
- up->z = 0;
- S3L_vec3Xmat4(up,&m);
- }
- }
- void S3L_initPixelInfo(S3L_PixelInfo *p)
- {
- p->x = 0;
- p->y = 0;
- p->barycentric[0] = S3L_FRACTIONS_PER_UNIT;
- p->barycentric[1] = 0;
- p->barycentric[2] = 0;
- p->modelIndex = 0;
- p->triangleIndex = 0;
- p->triangleID = 0;
- p->depth = 0;
- p->previousZ = 0;
- }
- void S3L_initModel3D(
- const S3L_Unit *vertices,
- S3L_Unit vertexCount,
- const S3L_Index *triangles,
- S3L_Index triangleCount,
- S3L_Model3D *model)
- {
- model->vertices = vertices;
- model->vertexCount = vertexCount;
- model->triangles = triangles;
- model->triangleCount = triangleCount;
- model->customTransformMatrix = 0;
- S3L_initTransform3D(&(model->transform));
- S3L_initDrawConfig(&(model->config));
- }
- void S3L_initScene(
- S3L_Model3D *models,
- S3L_Index modelCount,
- S3L_Scene *scene)
- {
- scene->models = models;
- scene->modelCount = modelCount;
- S3L_initCamera(&(scene->camera));
- }
- void S3L_initDrawConfig(S3L_DrawConfig *config)
- {
- config->backfaceCulling = 2;
- config->visible = 1;
- }
- #ifndef S3L_PIXEL_FUNCTION
- #error Pixel rendering function (S3L_PIXEL_FUNCTION) not specified!
- #endif
- static inline void S3L_PIXEL_FUNCTION(S3L_PixelInfo *pixel); // forward decl
- /** Serves to accelerate linear interpolation for performance-critical
- code. Functions such as S3L_interpolate require division to compute each
- interpolated value, while S3L_FastLerpState only requires a division for
- the initiation and a shift for retrieving each interpolated value.
- S3L_FastLerpState stores a value and a step, both scaled (shifted by
- S3L_FAST_LERP_QUALITY) to increase precision. The step is being added to the
- value, which achieves the interpolation. This will only be useful for
- interpolations in which we need to get the interpolated value in every step.
- BEWARE! Shifting a negative value is undefined, so handling shifting of
- negative values has to be done cleverly. */
- typedef struct
- {
- S3L_Unit valueScaled;
- S3L_Unit stepScaled;
- } S3L_FastLerpState;
- #define S3L_getFastLerpValue(state)\
- (state.valueScaled >> S3L_FAST_LERP_QUALITY)
- #define S3L_stepFastLerp(state)\
- state.valueScaled += state.stepScaled
- static inline S3L_Unit S3L_interpolateBarycentric(
- S3L_Unit value0,
- S3L_Unit value1,
- S3L_Unit value2,
- S3L_Unit barycentric[3])
- {
- return
- (
- (value0 * barycentric[0]) +
- (value1 * barycentric[1]) +
- (value2 * barycentric[2])
- ) / S3L_FRACTIONS_PER_UNIT;
- }
- void S3L_mapProjectionPlaneToScreen(
- S3L_Vec4 point,
- S3L_ScreenCoord *screenX,
- S3L_ScreenCoord *screenY)
- {
- *screenX =
- S3L_HALF_RESOLUTION_X +
- (point.x * S3L_HALF_RESOLUTION_X) / S3L_FRACTIONS_PER_UNIT;
- *screenY =
- S3L_HALF_RESOLUTION_Y -
- (point.y * S3L_HALF_RESOLUTION_X) / S3L_FRACTIONS_PER_UNIT;
- }
- void S3L_zBufferClear(void)
- {
- #if S3L_Z_BUFFER
- for (uint32_t i = 0; i < S3L_RESOLUTION_X * S3L_RESOLUTION_Y; ++i)
- S3L_zBuffer[i] = S3L_MAX_DEPTH;
- #endif
- }
- void S3L_stencilBufferClear(void)
- {
- #if S3L_STENCIL_BUFFER
- for (uint32_t i = 0; i < S3L_STENCIL_BUFFER_SIZE; ++i)
- S3L_stencilBuffer[i] = 0;
- #endif
- }
- void S3L_newFrame(void)
- {
- S3L_zBufferClear();
- S3L_stencilBufferClear();
- }
- void S3L_drawTriangle(
- S3L_Vec4 point0,
- S3L_Vec4 point1,
- S3L_Vec4 point2,
- S3L_Index modelIndex,
- S3L_Index triangleIndex)
- {
- S3L_PixelInfo p;
- S3L_initPixelInfo(&p);
- p.modelIndex = modelIndex;
- p.triangleIndex = triangleIndex;
- p.triangleID = (modelIndex << 16) | triangleIndex;
- S3L_Vec4 *tPointSS, *lPointSS, *rPointSS; /* points in Screen Space (in
- S3L_Units, normalized by
- S3L_FRACTIONS_PER_UNIT) */
- S3L_Unit *barycentric0; // bar. coord that gets higher from L to R
- S3L_Unit *barycentric1; // bar. coord that gets higher from R to L
- S3L_Unit *barycentric2; // bar. coord that gets higher from bottom up
- // sort the vertices:
- #define assignPoints(t,a,b)\
- {\
- tPointSS = &point##t;\
- barycentric2 = &(p.barycentric[t]);\
- if (S3L_triangleWinding(point##t.x,point##t.y,point##a.x,point##a.y,\
- point##b.x,point##b.y) >= 0)\
- {\
- lPointSS = &point##a; rPointSS = &point##b;\
- barycentric0 = &(p.barycentric[b]);\
- barycentric1 = &(p.barycentric[a]);\
- }\
- else\
- {\
- lPointSS = &point##b; rPointSS = &point##a;\
- barycentric0 = &(p.barycentric[a]);\
- barycentric1 = &(p.barycentric[b]);\
- }\
- }
- if (point0.y <= point1.y)
- {
- if (point0.y <= point2.y)
- assignPoints(0,1,2)
- else
- assignPoints(2,0,1)
- }
- else
- {
- if (point1.y <= point2.y)
- assignPoints(1,0,2)
- else
- assignPoints(2,0,1)
- }
- #undef assignPoints
- #if S3L_FLAT
- *barycentric0 = S3L_FRACTIONS_PER_UNIT / 3;
- *barycentric1 = S3L_FRACTIONS_PER_UNIT / 3;
- *barycentric2 = S3L_FRACTIONS_PER_UNIT - 2 * (S3L_FRACTIONS_PER_UNIT / 3);
- #endif
- p.triangleSize[0] = rPointSS->x - lPointSS->x;
- p.triangleSize[1] =
- (rPointSS->y > lPointSS->y ? rPointSS->y : lPointSS->y) - tPointSS->y;
- // now draw the triangle line by line:
- S3L_ScreenCoord splitY; // Y of the vertically middle point of the triangle
- S3L_ScreenCoord endY; // bottom Y of the whole triangle
- int splitOnLeft; /* whether splitY is the y coord. of left or right
- point */
- if (rPointSS->y <= lPointSS->y)
- {
- splitY = rPointSS->y;
- splitOnLeft = 0;
- endY = lPointSS->y;
- }
- else
- {
- splitY = lPointSS->y;
- splitOnLeft = 1;
- endY = rPointSS->y;
- }
- S3L_ScreenCoord currentY = tPointSS->y;
- /* We'll be using an algorithm similar to Bresenham line algorithm. The
- specifics of this algorithm are among others:
- - drawing possibly NON-CONTINUOUS line
- - NOT tracing the line exactly, but rather rasterizing one the right
- side of it, according to the pixel CENTERS, INCLUDING the pixel
- centers
-
- The principle is this:
- - Move vertically by pixels and accumulate the error (abs(dx/dy)).
- - If the error is greater than one (crossed the next pixel center), keep
- moving horizontally and substracting 1 from the error until it is less
- than 1 again.
- - To make this INTEGER ONLY, scale the case so that distance between
- pixels is equal to dy (instead of 1). This way the error becomes
- dx/dy * dy == dx, and we're comparing the error to (and potentially
- substracting) 1 * dy == dy. */
- int16_t
- /* triangle side:
- left right */
- lX, rX, // current x position on the screen
- lDx, rDx, // dx (end point - start point)
- lDy, rDy, // dy (end point - start point)
- lInc, rInc, // direction in which to increment (1 or -1)
- lErr, rErr, // current error (Bresenham)
- lErrCmp, rErrCmp, // helper for deciding comparison (> vs >=)
- lErrAdd, rErrAdd, // error value to add in each Bresenham cycle
- lErrSub, rErrSub; // error value to substract when moving in x direction
- S3L_FastLerpState lSideFLS, rSideFLS;
- #if S3L_COMPUTE_LERP_DEPTH
- S3L_FastLerpState lDepthFLS, rDepthFLS;
- #define initDepthFLS(s,p1,p2)\
- s##DepthFLS.valueScaled = p1##PointSS->z << S3L_FAST_LERP_QUALITY;\
- s##DepthFLS.stepScaled = ((p2##PointSS->z << S3L_FAST_LERP_QUALITY) -\
- s##DepthFLS.valueScaled) / (s##Dy != 0 ? s##Dy : 1);
- #else
- #define initDepthFLS(s,p1,p2) ;
- #endif
- /* init side for the algorithm, params:
- s - which side (l or r)
- p1 - point from (t, l or r)
- p2 - point to (t, l or r)
- down - whether the side coordinate goes top-down or vice versa */
- #define initSide(s,p1,p2,down)\
- s##X = p1##PointSS->x;\
- s##Dx = p2##PointSS->x - p1##PointSS->x;\
- s##Dy = p2##PointSS->y - p1##PointSS->y;\
- initDepthFLS(s,p1,p2)\
- s##SideFLS.stepScaled = (S3L_FRACTIONS_PER_UNIT << S3L_FAST_LERP_QUALITY)\
- / (s##Dy != 0 ? s##Dy : 1);\
- s##SideFLS.valueScaled = 0;\
- if (!down)\
- {\
- s##SideFLS.valueScaled =\
- S3L_FRACTIONS_PER_UNIT << S3L_FAST_LERP_QUALITY;\
- s##SideFLS.stepScaled *= -1;\
- }\
- s##Inc = s##Dx >= 0 ? 1 : -1;\
- if (s##Dx < 0)\
- {s##Err = 0; s##ErrCmp = 0;}\
- else\
- {s##Err = s##Dy; s##ErrCmp = 1;}\
- s##ErrAdd = S3L_abs(s##Dx);\
- s##ErrSub = s##Dy != 0 ? s##Dy : 1; /* don't allow 0, could lead to an
- infinite substracting loop */
- #define stepSide(s)\
- while (s##Err - s##Dy >= s##ErrCmp)\
- {\
- s##X += s##Inc;\
- s##Err -= s##ErrSub;\
- }\
- s##Err += s##ErrAdd;
- initSide(r,t,r,1)
- initSide(l,t,l,1)
- #if S3L_PERSPECTIVE_CORRECTION
- /* PC is done by linearly interpolating reciprocals from which the corrected
- velues can be computed. See
- http://www.lysator.liu.se/~mikaelk/doc/perspectivetexture/ */
- #if S3L_PERSPECTIVE_CORRECTION == 1
- #define Z_RECIP_NUMERATOR\
- (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT)
- #elif S3L_PERSPECTIVE_CORRECTION == 2
- #define Z_RECIP_NUMERATOR\
- (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT)
- #endif
- /* ^ This numerator is a number by which we divide values for the
- reciprocals. For PC == 2 it has to be lower because linear interpolation
- scaling would make it overflow -- this results in lower depth precision
- in bigger distance for PC == 2. */
- S3L_Unit
- tPointRecipZ, lPointRecipZ, rPointRecipZ, /* Reciprocals of the depth of
- each triangle point. */
- lRecip0, lRecip1, rRecip0, rRecip1; /* Helper variables for swapping
- the above after split. */
- tPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(tPointSS->z);
- lPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(lPointSS->z);
- rPointRecipZ = Z_RECIP_NUMERATOR / S3L_nonZero(rPointSS->z);
- lRecip0 = tPointRecipZ;
- lRecip1 = lPointRecipZ;
- rRecip0 = tPointRecipZ;
- rRecip1 = rPointRecipZ;
- #define manageSplitPerspective(b0,b1)\
- b1##Recip0 = b0##PointRecipZ;\
- b1##Recip1 = b1##PointRecipZ;\
- b0##Recip0 = b0##PointRecipZ;\
- b0##Recip1 = tPointRecipZ;
- #else
- #define manageSplitPerspective(b0,b1) ;
- #endif
- // clip to the screen in y dimension:
- endY = S3L_min(endY,S3L_RESOLUTION_Y);
- /* Clipping above the screen (y < 0) can't be easily done here, will be
- handled inside the loop. */
- while (currentY < endY) /* draw the triangle from top to bottom -- the
- bottom-most row is left out because, following
- from the rasterization rules (see start of the
- file), it is to never be rasterized. */
- {
- if (currentY == splitY) // reached a vertical split of the triangle?
- {
- #define manageSplit(b0,b1,s0,s1)\
- S3L_Unit *tmp = barycentric##b0;\
- barycentric##b0 = barycentric##b1;\
- barycentric##b1 = tmp;\
- s0##SideFLS.valueScaled = (S3L_FRACTIONS_PER_UNIT\
- << S3L_FAST_LERP_QUALITY) - s0##SideFLS.valueScaled;\
- s0##SideFLS.stepScaled *= -1;\
- manageSplitPerspective(s0,s1)
- if (splitOnLeft)
- {
- initSide(l,l,r,0);
- manageSplit(0,2,r,l)
- }
- else
- {
- initSide(r,r,l,0);
- manageSplit(1,2,l,r)
- }
- }
- stepSide(r)
- stepSide(l)
- if (currentY >= 0) /* clipping of pixels whose y < 0 (can't be easily done
- outside the loop because of the Bresenham-like
- algorithm steps) */
- {
- p.y = currentY;
- // draw the horizontal line
- #if !S3L_FLAT
- S3L_Unit rowLength = S3L_nonZero(rX - lX - 1); // prevent zero div
- #if S3L_PERSPECTIVE_CORRECTION
- S3L_Unit lOverZ, lRecipZ, rOverZ, rRecipZ, lT, rT;
- lT = S3L_getFastLerpValue(lSideFLS);
- rT = S3L_getFastLerpValue(rSideFLS);
- lOverZ = S3L_interpolateByUnitFrom0(lRecip1,lT);
- lRecipZ = S3L_interpolateByUnit(lRecip0,lRecip1,lT);
- rOverZ = S3L_interpolateByUnitFrom0(rRecip1,rT);
- rRecipZ = S3L_interpolateByUnit(rRecip0,rRecip1,rT);
- #else
- S3L_FastLerpState b0FLS, b1FLS;
- #if S3L_COMPUTE_LERP_DEPTH
- S3L_FastLerpState depthFLS;
- depthFLS.valueScaled = lDepthFLS.valueScaled;
- depthFLS.stepScaled =
- (rDepthFLS.valueScaled - lDepthFLS.valueScaled) / rowLength;
- #endif
- b0FLS.valueScaled = 0;
- b1FLS.valueScaled = lSideFLS.valueScaled;
- b0FLS.stepScaled = rSideFLS.valueScaled / rowLength;
- b1FLS.stepScaled = -1 * lSideFLS.valueScaled / rowLength;
- #endif
- #endif
- // clip to the screen in x dimension:
- S3L_ScreenCoord rXClipped = S3L_min(rX,S3L_RESOLUTION_X),
- lXClipped = lX;
- if (lXClipped < 0)
- {
- lXClipped = 0;
- #if !S3L_PERSPECTIVE_CORRECTION && !S3L_FLAT
- b0FLS.valueScaled -= lX * b0FLS.stepScaled;
- b1FLS.valueScaled -= lX * b1FLS.stepScaled;
- #if S3L_COMPUTE_LERP_DEPTH
- depthFLS.valueScaled -= lX * depthFLS.stepScaled;
- #endif
- #endif
- }
- #if S3L_PERSPECTIVE_CORRECTION
- S3L_ScreenCoord i = lXClipped - lX; /* helper var to save one
- substraction in the inner
- loop */
- #endif
- #if S3L_PERSPECTIVE_CORRECTION == 2
- S3L_FastLerpState
- depthPC, // interpolates depth between row segments
- b0PC, // interpolates barycentric0 between row segments
- b1PC; // interpolates barycentric1 between row segments
- /* ^ These interpolate values between row segments (lines of pixels
- of S3L_PC_APPROX_LENGTH length). After each row segment perspective
- correction is recomputed. */
- depthPC.valueScaled =
- (Z_RECIP_NUMERATOR /
- S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,i,rowLength)))
- << S3L_FAST_LERP_QUALITY;
- b0PC.valueScaled =
- (
- S3L_interpolateFrom0(rOverZ,i,rowLength)
- * depthPC.valueScaled
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- b1PC.valueScaled =
- (
- (lOverZ - S3L_interpolateFrom0(lOverZ,i,rowLength))
- * depthPC.valueScaled
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- int8_t rowCount = S3L_PC_APPROX_LENGTH;
- #endif
- #if S3L_Z_BUFFER
- uint32_t zBufferIndex = p.y * S3L_RESOLUTION_X + lXClipped;
- #endif
- // draw the row -- inner loop:
- for (S3L_ScreenCoord x = lXClipped; x < rXClipped; ++x)
- {
- int8_t testsPassed = 1;
- #if S3L_STENCIL_BUFFER
- if (!S3L_stencilTest(x,p.y))
- testsPassed = 0;
- #endif
- p.x = x;
- #if S3L_COMPUTE_DEPTH
- #if S3L_PERSPECTIVE_CORRECTION == 1
- p.depth = Z_RECIP_NUMERATOR /
- S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,i,rowLength));
- #elif S3L_PERSPECTIVE_CORRECTION == 2
- if (rowCount >= S3L_PC_APPROX_LENGTH)
- {
- // init the linear interpolation to the next PC correct value
- rowCount = 0;
- S3L_Unit nextI = i + S3L_PC_APPROX_LENGTH;
- if (nextI < rowLength)
- {
- S3L_Unit nextDepthScaled =
- (
- Z_RECIP_NUMERATOR /
- S3L_nonZero(S3L_interpolate(lRecipZ,rRecipZ,nextI,rowLength))
- ) << S3L_FAST_LERP_QUALITY;
- depthPC.stepScaled =
- (nextDepthScaled - depthPC.valueScaled) / S3L_PC_APPROX_LENGTH;
- S3L_Unit nextValue =
- (
- S3L_interpolateFrom0(rOverZ,nextI,rowLength)
- * nextDepthScaled
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- b0PC.stepScaled =
- (nextValue - b0PC.valueScaled) / S3L_PC_APPROX_LENGTH;
- nextValue =
- (
- (lOverZ - S3L_interpolateFrom0(lOverZ,nextI,rowLength))
- * nextDepthScaled
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- b1PC.stepScaled =
- (nextValue - b1PC.valueScaled) / S3L_PC_APPROX_LENGTH;
- }
- else
- {
- /* A special case where we'd be interpolating outside the triangle.
- It seems like a valid approach at first, but it creates a bug
- in a case when the rasaterized triangle is near screen 0 and can
- actually never reach the extrapolated screen position. So we
- have to clamp to the actual end of the triangle here. */
- S3L_Unit maxI = S3L_nonZero(rowLength - i);
- S3L_Unit nextDepthScaled =
- (
- Z_RECIP_NUMERATOR /
- S3L_nonZero(rRecipZ)
- ) << S3L_FAST_LERP_QUALITY;
- depthPC.stepScaled =
- (nextDepthScaled - depthPC.valueScaled) / maxI;
- S3L_Unit nextValue =
- (
- rOverZ
- * nextDepthScaled
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- b0PC.stepScaled =
- (nextValue - b0PC.valueScaled) / maxI;
- b1PC.stepScaled =
- -1 * b1PC.valueScaled / maxI;
- }
- }
- p.depth = S3L_getFastLerpValue(depthPC);
- #else
- p.depth = S3L_getFastLerpValue(depthFLS);
- S3L_stepFastLerp(depthFLS);
- #endif
- #else // !S3L_COMPUTE_DEPTH
- p.depth = (tPointSS->z + lPointSS->z + rPointSS->z) / 3;
- #endif
- #if S3L_Z_BUFFER
- p.previousZ = S3L_zBuffer[zBufferIndex];
- zBufferIndex++;
- if (!S3L_zTest(p.x,p.y,p.depth))
- testsPassed = 0;
- #endif
- if (testsPassed)
- {
- #if !S3L_FLAT
- #if S3L_PERSPECTIVE_CORRECTION == 0
- *barycentric0 = S3L_getFastLerpValue(b0FLS);
- *barycentric1 = S3L_getFastLerpValue(b1FLS);
- #elif S3L_PERSPECTIVE_CORRECTION == 1
- *barycentric0 =
- (
- S3L_interpolateFrom0(rOverZ,i,rowLength)
- * p.depth
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- *barycentric1 =
- (
- (lOverZ - S3L_interpolateFrom0(lOverZ,i,rowLength))
- * p.depth
- ) / (Z_RECIP_NUMERATOR / S3L_FRACTIONS_PER_UNIT);
- #elif S3L_PERSPECTIVE_CORRECTION == 2
- *barycentric0 = S3L_getFastLerpValue(b0PC);
- *barycentric1 = S3L_getFastLerpValue(b1PC);
- #endif
- *barycentric2 =
- S3L_FRACTIONS_PER_UNIT - *barycentric0 - *barycentric1;
- #endif
- S3L_PIXEL_FUNCTION(&p);
- } // tests passed
- #if !S3L_FLAT
- #if S3L_PERSPECTIVE_CORRECTION
- i++;
- #if S3L_PERSPECTIVE_CORRECTION == 2
- rowCount++;
-
- S3L_stepFastLerp(depthPC);
- S3L_stepFastLerp(b0PC);
- S3L_stepFastLerp(b1PC);
- #endif
- #else
- S3L_stepFastLerp(b0FLS);
- S3L_stepFastLerp(b1FLS);
- #endif
- #endif
- } // inner loop
- } // y clipping
- #if !S3L_FLAT
- S3L_stepFastLerp(lSideFLS);
- S3L_stepFastLerp(rSideFLS);
- #if S3L_COMPUTE_LERP_DEPTH
- S3L_stepFastLerp(lDepthFLS);
- S3L_stepFastLerp(rDepthFLS);
- #endif
- #endif
- ++currentY;
- } // row drawing
- #undef manageSplit
- #undef initPC
- #undef initSide
- #undef stepSide
- #undef Z_RECIP_NUMERATOR
- }
- void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle)
- {
- if (angle < S3L_SIN_TABLE_UNIT_STEP)
- return; // no visible rotation
- S3L_Unit angleSin = S3L_sin(angle);
- S3L_Unit angleCos = S3L_cos(angle);
- S3L_Unit xBackup = *x;
- *x =
- (angleCos * (*x)) / S3L_FRACTIONS_PER_UNIT -
- (angleSin * (*y)) / S3L_FRACTIONS_PER_UNIT;
- *y =
- (angleSin * xBackup) / S3L_FRACTIONS_PER_UNIT +
- (angleCos * (*y)) / S3L_FRACTIONS_PER_UNIT;
- }
- void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 *m)
- {
- S3L_makeScaleMatrix(
- worldTransform.scale.x,
- worldTransform.scale.y,
- worldTransform.scale.z,
- m
- );
- S3L_Mat4 t;
- S3L_makeRotationMatrixZXY(
- worldTransform.rotation.x,
- worldTransform.rotation.y,
- worldTransform.rotation.z,
- &t);
- S3L_mat4Xmat4(m,&t);
- S3L_makeTranslationMat(
- worldTransform.translation.x,
- worldTransform.translation.y,
- worldTransform.translation.z,
- &t);
- S3L_mat4Xmat4(m,&t);
- }
- void S3L_transposeMat4(S3L_Mat4 *m)
- {
- S3L_Unit tmp;
- for (uint8_t y = 0; y < 3; ++y)
- for (uint8_t x = 1 + y; x < 4; ++x)
- {
- tmp = (*m)[x][y];
- (*m)[x][y] = (*m)[y][x];
- (*m)[y][x] = tmp;
- }
- }
- void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 *m)
- {
- S3L_makeTranslationMat(
- -1 * cameraTransform.translation.x,
- -1 * cameraTransform.translation.y,
- -1 * cameraTransform.translation.z,
- m);
- S3L_Mat4 r;
- S3L_makeRotationMatrixZXY(
- cameraTransform.rotation.x,
- cameraTransform.rotation.y,
- cameraTransform.rotation.z,
- &r);
- S3L_transposeMat4(&r); // transposing creates an inverse transform
- S3L_mat4Xmat4(m,&r);
- }
- int8_t S3L_triangleWinding(
- S3L_ScreenCoord x0,
- S3L_ScreenCoord y0,
- S3L_ScreenCoord x1,
- S3L_ScreenCoord y1,
- S3L_ScreenCoord x2,
- S3L_ScreenCoord y2)
- {
- int32_t winding =
- (y1 - y0) * (x2 - x1) - (x1 - x0) * (y2 - y1);
- // ^ cross product for points with z == 0
- return winding > 0 ? 1 : (winding < 0 ? -1 : 0);
- }
- /**
- Checks if given triangle (in Screen Space) is at least partially visible,
- i.e. returns false if the triangle is either completely outside the frustum
- (left, right, top, bottom, near) or is invisible due to backface culling.
- */
- static inline int8_t S3L_triangleIsVisible(
- S3L_Vec4 p0,
- S3L_Vec4 p1,
- S3L_Vec4 p2,
- uint8_t backfaceCulling)
- {
- #define clipTest(c,cmp,v)\
- (p0.c cmp (v) && p1.c cmp (v) && p2.c cmp (v))
- if ( // outside frustum?
- #if S3L_NEAR_CROSS_STRATEGY == 0
- p0.z <= S3L_NEAR || p1.z <= S3L_NEAR || p2.z <= S3L_NEAR ||
- // ^ partially in front of NEAR?
- #else
- clipTest(z,<=,S3L_NEAR) || // completely in front of NEAR?
- #endif
- clipTest(x,<,0) ||
- clipTest(x,>=,S3L_RESOLUTION_X) ||
- clipTest(y,<,0) ||
- clipTest(y,>,S3L_RESOLUTION_Y)
- )
- return 0;
- #undef clipTest
- if (backfaceCulling != 0)
- {
- int8_t winding =
- S3L_triangleWinding(p0.x,p0.y,p1.x,p1.y,p2.x,p2.y);
- if ((backfaceCulling == 1 && winding > 0) ||
- (backfaceCulling == 2 && winding < 0))
- return 0;
- }
- return 1;
- }
- #if S3L_SORT != 0
- typedef struct
- {
- uint8_t modelIndex;
- S3L_Index triangleIndex;
- uint16_t sortValue;
- } _S3L_TriangleToSort;
- _S3L_TriangleToSort S3L_sortArray[S3L_MAX_TRIANGES_DRAWN];
- uint16_t S3L_sortArrayLength;
- #endif
- void _S3L_projectVertex(
- const S3L_Model3D *model,
- S3L_Index triangleIndex,
- uint8_t vertex,
- S3L_Mat4 *projectionMatrix,
- S3L_Vec4 *result)
- {
- uint32_t vertexIndex = model->triangles[triangleIndex * 3 + vertex] * 3;
- result->x = model->vertices[vertexIndex];
- result->y = model->vertices[vertexIndex + 1];
- result->z = model->vertices[vertexIndex + 2];
- result->w = S3L_FRACTIONS_PER_UNIT; // needed for translation
-
- S3L_vec3Xmat4(result,projectionMatrix);
- result->w = result->z;
- /* We'll keep the non-clamped z in w for sorting. */
- }
- void _S3L_mapProjectedVertexToScreen(S3L_Vec4 *vertex, S3L_Unit focalLength)
- {
- vertex->z = vertex->z >= S3L_NEAR ? vertex->z : S3L_NEAR;
- /* ^ This firstly prevents zero division in the follwoing z-divide and
- secondly "pushes" vertices that are in front of near a little bit forward,
- which makes them behave a bit better. If all three vertices end up exactly
- on NEAR, the triangle will be culled. */
- S3L_perspectiveDivide(vertex,focalLength);
-
- S3L_ScreenCoord sX, sY;
-
- S3L_mapProjectionPlaneToScreen(*vertex,&sX,&sY);
-
- vertex->x = sX;
- vertex->y = sY;
- }
- /**
- Projects a triangle to the screen. If enabled, a triangle can be potentially
- subdivided into two if it crosses the near plane, in which case two projected
- triangles are returned (return value will be 1).
- */
- uint8_t _S3L_projectTriangle(
- const S3L_Model3D *model,
- S3L_Index triangleIndex,
- S3L_Mat4 *matrix,
- uint32_t focalLength,
- S3L_Vec4 transformed[6])
- {
- _S3L_projectVertex(model,triangleIndex,0,matrix,&(transformed[0]));
- _S3L_projectVertex(model,triangleIndex,1,matrix,&(transformed[1]));
- _S3L_projectVertex(model,triangleIndex,2,matrix,&(transformed[2]));
- uint8_t result = 0;
- #if S3L_NEAR_CROSS_STRATEGY == 2
- uint8_t infront = 0;
- uint8_t behind = 0;
- uint8_t infrontI[3];
- uint8_t behindI[3];
- for (uint8_t i = 0; i < 3; ++i)
- if (transformed[i].z < S3L_NEAR)
- {
- infrontI[infront] = i;
- infront++;
- }
- else
- {
- behindI[behind] = i;
- behind++;
- }
- #define interpolateVertex \
- S3L_Unit ratio =\
- ((transformed[be].z - S3L_NEAR) * S3L_FRACTIONS_PER_UNIT) /\
- (transformed[be].z - transformed[in].z);\
- transformed[in].x = transformed[be].x - \
- ((transformed[be].x - transformed[in].x) * ratio) /\
- S3L_FRACTIONS_PER_UNIT;\
- transformed[in].y = transformed[be].y -\
- ((transformed[be].y - transformed[in].y) * ratio) /\
- S3L_FRACTIONS_PER_UNIT;\
- transformed[in].z = S3L_NEAR;
-
- if (infront == 2)
- {
- // shift the two vertices forward along the edge
- for (uint8_t i = 0; i < 2; ++i)
- {
- uint8_t be = behindI[0], in = infrontI[i];
-
- interpolateVertex
- }
- }
- else if (infront == 1)
- {
- // create another triangle and do the shifts
- transformed[3] = transformed[behindI[1]];
- transformed[4] = transformed[infrontI[0]];
- transformed[5] = transformed[infrontI[0]];
- for (uint8_t i = 0; i < 2; ++i)
- {
- uint8_t be = behindI[i], in = i + 4;
- interpolateVertex
- }
- transformed[infrontI[0]] = transformed[4];
- _S3L_mapProjectedVertexToScreen(&transformed[3],focalLength);
- _S3L_mapProjectedVertexToScreen(&transformed[4],focalLength);
- _S3L_mapProjectedVertexToScreen(&transformed[5],focalLength);
- result = 1;
- }
- #undef interpolateVertex
- #endif // S3L_NEAR_CROSS_STRATEGY == 2
- _S3L_mapProjectedVertexToScreen(&transformed[0],focalLength);
- _S3L_mapProjectedVertexToScreen(&transformed[1],focalLength);
- _S3L_mapProjectedVertexToScreen(&transformed[2],focalLength);
- return result;
- }
- void S3L_drawScene(S3L_Scene scene)
- {
- S3L_Mat4 matFinal, matCamera;
- S3L_Vec4 transformed[6]; // transformed triangle coords, for 2 triangles
- const S3L_Model3D *model;
- S3L_Index modelIndex, triangleIndex;
- S3L_makeCameraMatrix(scene.camera.transform,&matCamera);
- #if S3L_SORT != 0
- uint16_t previousModel = 0;
- S3L_sortArrayLength = 0;
- #endif
- for (modelIndex = 0; modelIndex < scene.modelCount; ++modelIndex)
- {
- if (!scene.models[modelIndex].config.visible)
- continue;
- #if S3L_SORT != 0
- if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
- break;
- previousModel = modelIndex;
- #endif
- if (scene.models[modelIndex].customTransformMatrix == 0)
- S3L_makeWorldMatrix(scene.models[modelIndex].transform,&matFinal);
- else
- {
- S3L_Mat4 *m = scene.models[modelIndex].customTransformMatrix;
- for (int8_t j = 0; j < 4; ++j)
- for (int8_t i = 0; i < 4; ++i)
- matFinal[i][j] = (*m)[i][j];
- }
- S3L_mat4Xmat4(&matFinal,&matCamera);
- S3L_Index triangleCount = scene.models[modelIndex].triangleCount;
- triangleIndex = 0;
-
- while (triangleIndex < triangleCount)
- {
- model = &(scene.models[modelIndex]);
- /* Some kind of cache could be used in theory to not project perviously
- already projected vertices, but after some testing this was abandoned,
- no gain was seen. */
- uint8_t split = _S3L_projectTriangle(model,triangleIndex,&matFinal,
- scene.camera.focalLength,transformed);
- if (S3L_triangleIsVisible(transformed[0],transformed[1],transformed[2],
- model->config.backfaceCulling))
- {
- #if S3L_SORT == 0
- // without sorting draw right away
- S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
- triangleIndex);
- if (split) // draw potential subtriangle
- S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
- modelIndex, triangleIndex);
- #else
- S3L_UNUSED(split);
- if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
- break;
- // with sorting add to a sort list
- S3L_sortArray[S3L_sortArrayLength].modelIndex = modelIndex;
- S3L_sortArray[S3L_sortArrayLength].triangleIndex = triangleIndex;
- S3L_sortArray[S3L_sortArrayLength].sortValue = S3L_zeroClamp(
- transformed[0].w + transformed[1].w + transformed[2].w) >> 2;
- /* ^
- The w component here stores non-clamped z.
-
- As a simple approximation we sort by the triangle center point,
- which is a mean coordinate -- we don't actually have to divide by 3
- (or anything), that is unnecessary for sorting! We shift by 2 just
- as a fast operation to prevent overflow of the sum over uint_16t. */
- S3L_sortArrayLength++;
- #endif
- }
- triangleIndex++;
- }
- }
- #if S3L_SORT != 0
- #if S3L_SORT == 1
- #define cmp <
- #else
- #define cmp >
- #endif
- /* Sort the triangles. We use insertion sort, because it has many advantages,
- especially for smaller arrays (better than bubble sort, in-place, stable,
- simple, ...). */
- for (int16_t i = 1; i < S3L_sortArrayLength; ++i)
- {
- _S3L_TriangleToSort tmp = S3L_sortArray[i];
-
- int16_t j = i - 1;
- while (j >= 0 && S3L_sortArray[j].sortValue cmp tmp.sortValue)
- {
- S3L_sortArray[j + 1] = S3L_sortArray[j];
- j--;
- }
- S3L_sortArray[j + 1] = tmp;
- }
- #undef cmp
- for (S3L_Index i = 0; i < S3L_sortArrayLength; ++i) // draw sorted triangles
- {
- modelIndex = S3L_sortArray[i].modelIndex;
- triangleIndex = S3L_sortArray[i].triangleIndex;
- model = &(scene.models[modelIndex]);
- if (modelIndex != previousModel)
- {
- // only recompute the matrix when the model has changed
- S3L_makeWorldMatrix(model->transform,&matFinal);
- S3L_mat4Xmat4(&matFinal,&matCamera);
- previousModel = modelIndex;
- }
- /* Here we project the points again, which is redundant and slow as they've
- already been projected above, but saving the projected points would
- require a lot of memory, which for small resolutions could be even
- worse than z-bufer. So this seems to be the best way memory-wise. */
- uint8_t split = _S3L_projectTriangle(model,triangleIndex,&matFinal,
- scene.camera.focalLength,transformed);
- S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
- triangleIndex);
-
- if (split)
- S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
- modelIndex, triangleIndex);
- }
- #endif
- }
- #endif // guard
|