Main Menu

Jelly Sinus Terrain

Started by kevin, May 02, 2025, 12:39:57 PM

Previous topic - Next topic

kevin

🧪 Jelly Sinus Terrain - How It Works (Step-by-Step Guide)

By PlayBASIC Tutor
Created: May 2025

This demo renders a jelly-like, animated sinusoidal terrain using a grid of rotating, Gouraud-shaded polygons. The "wobble" is based on each vertex's distance from the center of the grid and animated in real time.







🔧 1. Setup Constants and Data Types

* `WorldSIZE#` defines how wide the virtual world is.
* `Size` controls the grid resolution.
* A typed array `Terrain()` stores the 3D coordinates and color for each vertex.

PlayBASIC Code: [Select]
Type tVertex
x#, y#, z# ; Original 3D position
rx#, ry#, rz# ; Rotated/projected 3D position
Colour ; Vertex color
EndType





🌈 2. Create a Grayscale Palette

We generate a grayscale palette based on depth. This will be used later to shade the polygons based on how far away they are.

PlayBASIC Code: [Select]
for lp = 0 to depth
Scaler# = (depth - lp) / float(depth)
ThisRGB = cliprange(Scaler# * 255, 0, 255)
palette(lp) = rgb(ThisRGB, ThisRGB, ThisRGB)
next





🎨 3. Draw the Backdrop

We create a shaded backdrop image using `shadebox`. This gives a nicer visual background behind the terrain.

PlayBASIC Code: [Select]
BackDrop = NewImage(...)
rendertoimage BackDrop
shadebox ...
rendertoscreen





🔁 4. Main Loop

Each frame, we:

* Draw the backdrop
* Capture 3D scene to render
* Update tilt and turn angles for camera rotation
* Recalculate the terrain heights
* Rotate and project the terrain
* Render the terrain polygons

PlayBASIC Code: [Select]
tilt# += 0.01
turn# += 2.3
RenderGround(Terrain(), WORLDSIZE#)
RotateVerts(Terrain(), Tilt#, Turn#, Roll#)
Render(Terrain())





### 🏔� 5. **RenderGround() – Build the Heightmap**

This function fills the `Terrain()` array with X/Z positions in a grid, and the Y (height) is calculated using a sinus function based on distance from the center. The result is a radial wave (like ripples in water).

PlayBASIC Code: [Select]
Map(xlp,zlp).y# = 1000 - (sin(baseangle# + angle#) * (1000 * (1 - Distance#)))



* `baseangle#` is animated each frame to make the wave move.
* `scaler#` affects wave frequency.



🌀 6. RotateVerts() – Rotate and Project Vertices

Each 3D point is rotated using a 3x3 rotation matrix for tilt, turn, and roll. After rotation, it's projected to 2D screen space using perspective projection.

PlayBASIC Code: [Select]
rx# = screenX
ry# = screenY
rz# = depthZ



This is where the terrain "wobbles" and appears 3D.



🔺 7. **Render() – Draw the Polygons

We loop over the grid of vertices and draw **two triangles** for each quad (square) using `gouraudtri`.

Each vertex's color is affected by depth (z) using the earlier-created palette, giving us a 3D shaded look.

PlayBASIC Code: [Select]
Rgb1 = RgbAlphaMult(Rgb1, ShadeRGB)
gouraudtri ...



There's also a bonus "depth ray" line drawn for fun.



📌 Notes and Tips

* The terrain "jelly" effect is created using a sinusoidal function that changes each frame, causing the wave to pulse out from the center.
* The terrain rotates and zooms in/out smoothly with perspective.
* This demo uses a single layer of polygons. It's fast because it avoids overdraw and depth-sorting issues.
* Don't store original and rotated values in the same array in real projects. This is just for demo simplicity.



🧠 What New Programmers Can Learn

* How to use typed arrays for storing 3D vertex data
* How to build and animate a height map
* How to rotate and project 3D points to 2D
* How to shade polygons based on depth
* How sinus functions can be used for cool animation effects




PlayBASIC Code: [Select]
; PROJECT : Lerp Ground
; AUTHOR : PlayBASIC Tutor - https://playbasic.com
; CREATED : 2/05/2025
; EDITED : 3/05/2025
; ---------------------------------------------------------------------


WorldSIZE# = 7500
Size = 64

Type tVertex
x#,y#,z#
rx#,ry#,rz#
Colour
EndTYPE

Dim Terrain(Size,Size) as tVertex

depth =15000

Camera = newcamera()
cameracls camera,off
Scenecachesize 1000*1000 *8
scenemaxzdepth depth

dim palette(depth)
for lp =0 to depth
Scaler# = (depth-lp)/float(depth)
ThisRGB = cliprange(Scaler#*255,0,255)
palette(lp) = rgb(ThisRGB,ThisRGB,ThisRGB)
next


BackDrop=NewImage(GetScreenWidth(),GetSCreenHeight())
rendertoimage BackDrop
// Draw the shaded backdrop
c1=$223344
c2=c1*2
shadebox 0,0,getscreenwidth(),getscreenheight(),c1,c1,c2,c2

rendertoscreen


;setfps 30

// ------------------------------------------------
do
// ------------------------------------------------

drawimage backdrop,0,0,false
setcursor 0,0

// set PlayBASIC to capture mode for
capturetoscene
clsscene

tilt#+=0.01
turn#+=2.3

RenderGround(Terrain(), WORLDSIZE# )
RotateVerts(Terrain(),Tilt#,Turn#,Roll#)
Render(Terrain())
inkmode 1
drawcamera camera

thisrgb=point(0,0)

Sync

// ------------------------------------------------
loop spacekey()
// ------------------------------------------------



explicit true


Function Render(Map().tVertex )

local Size =getArrayelements(Map().tVertex,1)
local ThisRGB =point(0,0)

local sw=getscreenwidth()/2
local sh=getscreenheight()*0.1

local CLipZ1# = 10
local xlp,zlp,polygons

local ThisRGB =$ffffff
local DepthColour = $223344

local rgb1,rgb2,rgb3,rgb4

for zlp=0 to size-1
local zlp2=zlp+1

local x2# =Map(1,zlp).rx#
local y2# =Map(1,zlp).ry#
local z2# =Map(1,zlp).rz#


local x3# =Map(1,zlp2).rx#
local y3# =Map(1,zlp2).ry#
local z3# =Map(1,zlp2).rz#


local Col1 =map(0,zlp).Colour
local Col2 =map(1,zlp).Colour
local Col3 =map(1,zlp2).Colour
local Col4 =map(0,zlp2).Colour


for xlp=0 to size-1

local x1# =x2#
local y1# =y2#
local z1# =z2#

local x4# =x3#
local y4# =y3#
local z4# =z3#

local xlp2=xlp+1
x2# =Map(xlp2,zlp).rx#
y2# =Map(xlp2,zlp).ry#
z2# =Map(xlp2,zlp).rz#

x3# =Map(xlp2,zlp2).rx#
y3# =Map(xlp2,zlp2).ry#
z3# =Map(xlp2,zlp2).rz#

if (z1#< CLipZ1#) or (z2#< CLipZ1#) then continue
if (z3#< CLipZ1#) or (z4#< CLipZ1#) then continue

local rgb1 =Col2
local rgb4 =Col3

col2 =map(xlp2,zlp).Colour
col3 =map(xlp2,zlp2).Colour

local z#=(z1#+z2#+z3#)
local ShadeRGB = palette((z#+z4#)/4)
Rgb1=RgbAlphaMult(Rgb1,ShadeRGB)
Rgb2=RgbAlphaMult(Col2,ShadeRGB)
Rgb3=RgbAlphaMult(Col3,ShadeRGB)
Rgb4=RgbAlphaMult(Rgb4,ShadeRGB)

Login required to view complete source code