Script Library: 1213 scripts
 

r3d2.r

REBOL [ Title: "r3D" Author: [ "Massimiliano Vessi" "Andrew Hoadley" "François Jouen"] Email: %maxint--tiscali--it Date: 09-Oct-2012 version: 2.2.6 file: %r3d2.r Purpose: {"3D Library, you can use also images. Look a the end of the file to see how are made 3D model.} ;following data are for www.rebol.org library ;you can find a lot of rebol script there library: [ level: 'intermidiate platform: 'all type: [ tool] domain: [animation external-library graphics] tested-under: [windows linux] support: none license: [gpl] see-also: none ] ] ; Copyright (c) 2006, Andrew Hoadley ; Permission is hereby granted, free of charge, to any person obtaining a copy of this software ; and associated documentation files (the "Software"), to deal in the Software without restriction, ; including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, ; and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, ; subject to the following conditions: ; The above copyright notice and this permission notice shall be included in all copies or substantial ; portions of the Software. ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT ; NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES ; OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ; --------------------------------------------------------- ; Contents of r3d-matrix.r - matrix and vector library ; ; create different types of transformations r3d-identity: [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ] r3d-perspective: func [ "Create a perspective matrix with a vanishing point d units from the camera" d [number!] "d is the distance to the vanishing point - don't set it to zero !!" ][reduce [ 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 / d 1 ] ];end of r3d-perspective r3d-translate: func [ "Create a translation matrix" X [number!] "translate X units along the x axis" Y [number!] "translate Y units along the y axis" Z [number!] "translate Z units along the z axis" ][ reduce [ 1 0 0 X 0 1 0 Y 0 0 1 Z 0 0 0 1 ] ]; end of r3d-translate r3d-scale: func [ "Create a scale matrix" X [number!] "scale object by a factor of X along the x axis" Y [number!] "scale object by a factor of Y along the y axis" Z [number!] "scale object by a factor of Y along the z axis" ][ reduce [ X 0 0 0 0 Y 0 0 0 0 Z 0 0 0 0 1 ] ]; end of r3d-scale r3d-rotateX: func [ X [number!] ][ ; calculate once and store the sin and cos values sineX: sine X cosineX: cosine X reduce [ 1 0 0 0 0 cosineX (- sineX) 0 0 sineX cosineX 0 0 0 0 1 ] ]; end of r3d-rotateX r3d-rotateY: func [ Y [number!] ][ ; calculate once and store the sin and cos values sineY: sine Y cosineY: cosine Y reduce [ cosineY 0 sineY 0 0 1 0 0 (- sineY) 0 cosineY 0 0 0 0 1 ] ]; end of r3d-rotateY r3d-rotateZ: func [ Z [number!] ][ ; calculate once and store the sin and cos values sineZ: sine Z cosineZ: cosine Z reduce [ cosineZ (- sineZ) 0 0 sineZ cosineZ 0 0 0 0 1 0 0 0 0 1 ] ]; end of r3d-rotateZ r3d-face-direction: func [ DOF [block!] "Vector3 specifying direction of flight" up [block!] "Up vector may be in any direction except parallel to DOF" /local xaxis yaxis zaxis ][ zaxis: r3d-normalise DOF xaxis: r3d-normalise r3d-crossproduct up dOF yaxis: r3d-normalise r3d-crossproduct DOF xaxis reduce [ xaxis/1 yaxis/1 zaxis/1 0 xaxis/2 yaxis/2 zaxis/2 0 xaxis/3 yaxis/3 zaxis/3 0 0 0 0 1 ] ];end of r3d-face-direction r3d-position-object: func [ object_position [block!] ; all 3 parameters are 3 dimensional vectors position_to_look_at [block!] up [block!] /local DOF ][ DOF: r3d-Subtract position_to_look_at object_position zaxis: r3d-normalise DOF xaxis: r3d-normalise r3d-crossproduct DOF up yaxis: r3d-normalise r3d-crossproduct DOF xaxis reduce [ xaxis/1 yaxis/1 zaxis/1 object_position/1 xaxis/2 yaxis/2 zaxis/2 object_position/2 xaxis/3 yaxis/3 zaxis/3 object_position/3 0 0 0 1 ] ]; endo of r3d-position-object r3d-m4xm4: func [ "Matrix product between two 4x4 matrices" m1 [block!] "First matrix, length must be 16 (4x4)" m2 [block!] "Second matrix, length must be 16 (4x4)" /local result ][ result: copy [] foreach [a b c d] m1 [ append result reduce [ (a * m2/1) + (b * m2/5) + (c * m2/9) + (d * m2/13) (a * m2/2) + (b * m2/6) + (c * m2/10) + (d * m2/14) (a * m2/3) + (b * m2/7) + (c * m2/11) + (d * m2/15) (a * m2/4) + (b * m2/8) + (c * m2/12) + (d * m2/16) ] ] result ]; end of r3d-m4xm4 r3d-m4xv3: func [ "Matrix product between a 4x4 matrix and a 4x1 ([A B C 1]) vector. This function will append the last 1 to the vector 3x1" m [block!] "A 4x4 metrix (length 16)" v [block!] " A 3x1 vector, this function will tranform it in a 4x1 appnding a 1" /local result ][ tempres: copy [] result: copy [] foreach [a b c d] m [ append tempres reduce [ (a * v/1) + (b * v/2) + (c * v/3) + d ] ] ; create 1/w so we only divide once and then multiply by a fraction (faster) inv-w: reduce (1 / tempres/4) append result reduce tempres/1 * inv-w append result reduce tempres/2 * inv-w append result reduce tempres/3 * inv-w append result 1 return result ];end of r3d-m4xv3 r3d-m4xv3-array: func [ "Thi function return the same block of vertices, multiplied for the transformation matrix" vertices [block!] "This must be a block of vertices coordinates, like [ [ 1 2 3] [-2 5.2 4] ]" m4 [block!] "This is a 4x4 matrix" /local result vertex ][ result: copy [] foreach vertex vertices [ append result reduce [ r3d-m4xv3 m4 vertex ] ] return result ]; end of r3d-m4xv3-array ;Is this to compose all translation and rotation? r3d-compose-m4: func [ "Take a block containing 4x4 matrices and moltiplicate each other obtaining a single 4x4 matrix" matrixlist [block!] ; a list of at least 1 4x4 matrix /local result ][ cm4-len: length? matrixlist ; if there are no entries return the identity matrix if cm4-len = 0 [ return r3d-identity ] ; initialise the result with the first entry in the list result: copy matrixlist/1 ; if there is one entry return that entry if cm4-len = 1 [ return result ] ; for each rentry past the first for cm4-i 2 cm4-len 1 [ ;multiply the previous result by the next element result: r3d-m4xm4 pick matrixlist cm4-i result ] return result ]; end of r3d-compose-m4 r3d-transpose-m4: func [ "Transpose a 4x4 matrix" m [block!] "Matrix to transpose (4x4)" /local result ][ ; transpose a 4x4 matrix result: reduce [ m/1 m/5 m/9 m/13 m/2 m/6 m/10 m/14 m/3 m/7 m/11 m/15 m/4 m/8 m/12 m/16] ] r3d-inverse-m4: func [ "Inverse of a 4x4 matrix" m [block!] "Matrix 4x4" /local result pairs m4inv-res det ][ ; ok here we go... ; calculate pairs for first 8 elements pairs: reduce [ m/11 * m/16 m/12 * m/15 m/10 * m/16 m/12 * m/14 m/10 * m/15 m/11 * m/14 m/9 * m/16 m/12 * m/13 m/9 * m/15 m/11 * m/13 m/9 * m/14 m/10 * m/13 ] ; calculate first 8 elements m4inv-res: reduce [ ((pairs/1 * m/6) + (pairs/4 * m/7) + (pairs/5 * m/8)) - ((pairs/2 * m/6) + (pairs/3 * m/7) + (pairs/6 * m/8)) ((pairs/2 * m/5) + (pairs/7 * m/7) + (pairs/10 * m/8)) - ((pairs/1 * m/5) + (pairs/8 * m/7) + (pairs/9 * m/8)) ((pairs/3 * m/5) + (pairs/8 * m/6) + (pairs/11 * m/8)) - ((pairs/4 * m/5) + (pairs/7 * m/6) + (pairs/12 * m/8)) ((pairs/6 * m/5) + (pairs/9 * m/6) + (pairs/12 * m/7)) - ((pairs/5 * m/5) + (pairs/10 * m/6) + (pairs/11 * m/7)) ((pairs/2 * m/2) + (pairs/3 * m/3) + (pairs/6 * m/4)) - ((pairs/1 * m/2) + (pairs/4 * m/3) + (pairs/5 * m/4)) ((pairs/1 * m/1) + (pairs/8 * m/3) + (pairs/9 * m/4)) - ((pairs/2 * m/1) + (pairs/7 * m/3) + (pairs/10 * m/4)) ((pairs/4 * m/1) + (pairs/7 * m/2) + (pairs/12 * m/4)) - ((pairs/3 * m/1) + (pairs/8 * m/2) + (pairs/11 * m/4)) ((pairs/5 * m/1) + (pairs/10 * m/2) + (pairs/11 * m/3)) - ((pairs/6 * m/1) + (pairs/9 * m/2) + (pairs/12 * m/3)) ] ; calculate pairs for second 8 elements pairs: reduce [ m/3 * m/8 m/4 * m/7 m/2 * m/8 m/4 * m/6 m/2 * m/7 m/3 * m/6 m/1 * m/8 m/4 * m/5 m/1 * m/7 m/3 * m/5 m/1 * m/6 m/2 * m/5 ] ; calculate second 8 elements (cofactors) m4inv-res: append m4inv-res reduce [ ((pairs/1 * m/14) + (pairs/4 * m/15) + (pairs/5 * m/16)) - ((pairs/2 * m/14) + (pairs/3 * m/15) + (pairs/6 * m/16)) ((pairs/2 * m/13) + (pairs/7 * m/15) + (pairs/10 * m/16)) - ((pairs/1 * m/13) + (pairs/8 * m/15) + (pairs/9 * m/16)) ((pairs/3 * m/13) + (pairs/8 * m/14) + (pairs/11 * m/16)) - ((pairs/4 * m/13) + (pairs/7 * m/14) + (pairs/12 * m/16)) ((pairs/6 * m/13) + (pairs/9 * m/14) + (pairs/12 * m/15)) - ((pairs/5 * m/13) + (pairs/10 * m/14) + (pairs/11 * m/15)) ((pairs/3 * m/11) + (pairs/6 * m/12) + (pairs/2 * m/10)) - ((pairs/5 * m/12) + (pairs/1 * m/10) + (pairs/4 * m/11)) ((pairs/9 * m/12) + (pairs/1 * m/9) + (pairs/8 * m/11)) - ((pairs/7 * m/11) + (pairs/10 * m/12) + (pairs/2 * m/9)) ((pairs/7 * m/10) + (pairs/12 * m/12) + (pairs/4 * m/9)) - ((pairs/11 * m/12) + (pairs/3 * m/9) + (pairs/8 * m/10)) ((pairs/11 * m/11) + (pairs/5 * m/9) + (pairs/10 * m/10)) - ((pairs/9 * m/10) + (pairs/12 * m/11) + (pairs/6 * m/9)) ] ; calculate determinate det: (m/1 * m4inv-res/1) + (m/2 * m4inv-res/2) + (m/3 * m4inv-res/3) + (m/4 * m4inv-res/4) ; invert to avoid doing multiple divisions det: 1.0 / det result: copy [] foreach x m4inv-res [ append result reduce ( x * det ) ] return r3d-transpose-m4 result ] ; end of r3d-inverse-m4 r3d-dotproduct: func [ "Returns the dot product (a number) between two 3x1 vector" v1 [block!] "Vector (3x1)" v2 [block!] "Vector (3x1)" /local result ][result: reduce (v1/1 * v2/1) + (v1/2 * v2/2) + (v1/3 * v2/3)] r3d-crossproduct: func [ "Returns a cross product (vector, 3x1) between 2 vectors (3x1)" v1 [block!] "3x1 vector" v2 [block!] "3x1 vector" /local result ][ result: reduce [ (v1/2 * v2/3) - (v1/3 * v2/2) (v1/3 * v2/1) - (v1/1 * v2/3) (v1/1 * v2/2) - (v1/2 * v2/1) ] return result ] r3d-length: func [ "Returns the disctance between origin and a point (3x1)" v [block!] "Point coordinate (3x1)" /local result ][ result: reduce square-root ( (v/1 * v/1) + (v/2 * v/2) + (v/3 * v/3) ) ] r3d-Add: func [ "Add a to b - a and b can be either matrices or vectors but types must match" a [block!] b [block!] /local item result ][ result: copy [] for item 1 length? a 1 [ append result reduce ( pick a item ) + ( pick b item ) ] return result ] r3d-Subtract: func [ "Subtract b from a - a and b can be either matrices or vectors but types must match" a [block!] b [block!] /local item result ][ result: copy [] for item 1 length? a 1 [ append result reduce ( pick a item ) - ( pick b item ) ] return result ] r3d-Multiply: func [ "Multiply a by n - a and b can be either a matrix or vector, n is decimal" a [block!] n [decimal!] /local item result ][ result: copy [] for item 1 length? a 1 [ append result reduce ( pick a item ) * n ] return result ] r3d-Divide: func [ "Divide a by n - a and b can be either a matrix or vector, n is non-zero decimal" a [block!] n [decimal!] /local item result div ][ result: copy [] if n = 0.0 [ Print "Divide by zero attempted" return a ] div: 1.0 / n for item 1 length? a 1 [ append result reduce ( pick a item ) * div ] return result ] r3d-normalise: func [ "Normalize a vector, its module becomes 1" v [block!] "Vector 3x1" /local result ][ ; try to normalise a zero vector and you will get a zero vector back len: r3d-length v either ( len = 0 ) [ result: [ 0.0 0.0 0.0 ] ] [ result: reduce [ v/1 / len v/2 / len v/3 / len ] ] return result ] ;Debug functions? r3d-print-m4: func [ m [block!] ][ foreach [ a b c d ] m [ print [ a b c d ] ] print [] ] r3d-print-v3: func [ v [block!] ][ foreach elem v [ print elem ] print [] ] ;------------------------------------------------- ; ; contents of r3d-engine.r - r3d render engine ; Render: func [ "Main function that render 3D objects creating a VID DRAW" world [block!] "List of all object to render and their position" camera [block!] "Camera position, where it look and where is the UP" projection [block!] "Type of projection to use, 4x4 matrix, in doubt use: r3d-perspective 250. You can create funny distortions..." canvasSize [pair!] "Window size of the rendering" /no-cull /local result transvert trans2d model modelworld triangles ][ result: copy [] triangles: copy [] cameraInverse: r3d-inverse-m4 camera foreach r3dobject world [ model: r3dObject/1 modelworld: r3dobject/2 objcolor: r3dObject/3 ModelCamera: r3d-m4xm4 cameraInverse modelWorld ; transform the vertices into 3d space relative to the camera transVert: r3d-m4xv3-array model/1 modelcamera faces: model/2 ; faceinfo contains 2 blocks of n entries, a) face normals and b) furthest Z offset faceInfo: r3d-CalculateFaceNormals transVert faces ; transform the vertices again using the projection matrix trans2d: r3d-m4xv3-array transvert projection ;culling or not back face culling? either no-cull [append triangles r3d-Render2dTriangles-simple/no-cull trans2d faces faceInfo canvasSize objcolor ][append triangles r3d-Render2dTriangles-simple trans2d faces faceInfo canvasSize objcolor] ] ; depth sort triangles: sort/reverse triangles ;probe triangles foreach triangle triangles [ fillcolour: last triangle temptriang: copy triangle remove temptriang reverse temptriang remove temptriang reverse temptriang either tuple? fillcolour [ ; face is filled with a colour append result reduce ['pen fillcolour 'fill-pen fillcolour ] append result 'polygon foreach item temptriang [ append result item ] ][ ;image case either (length? temptriang) = 4 [ append result 'image foreach item temptriang [ append result item ] append result fillcolour ][ append result reduce ['pen none 'fill-pen fillcolour ] append result 'polygon foreach item temptriang [ append result item ] ] ] ;append result 'polygon ;append result triangle/2 ;append result triangle/3 ;append result triangle/4 ] ;print form result return result ] r3d-CalculateFaceNormals: func [ vertices [block!] faces [block!] /local result depthvals v1 v2 v3 vtmp1 vtmp2 vcp largest ][ result: copy [] depthvals: copy [] foreach face faces [ ; get the vertices pointed to by each index in the face v1: pick vertices face/1 v2: pick vertices face/2 v3: pick vertices face/3 ; get face normal vtmp1: r3d-subtract v2 v1 vtmp2: r3d-subtract v3 v2 vcp: r3d-crossproduct vtmp1 vtmp2 vcp: r3d-normalise vcp append/only result vcp ; get furthest z co-ord largest: -10000.0 if v1/3 > largest [ largest: v1/3 ] if v2/3 > largest [ largest: v2/3 ] if v3/3 > largest [ largest: v3/3 ] append depthvals largest ] reduce [ result depthvals ] ] r3d-Render2dTriangles-Simple: func [ transformedVertices [block!] faces [block!] faceInfo [block!] canvasSize [pair!] objColor [tuple! image!] /no-cull /local result temptriangle v face index origin count facez ][ result: copy [] ; todo - accurate lighting ; determine the origin origin: (canvasSize * 0.5) faceNormals: faceInfo/1 depthvals: faceinfo/2 count: 1 foreach face faces [ ; check if this face needs to be backface culled facenormal: pick faceNormals count ;print ["facenormal: " facenormal] depthval: pick depthvals count ;print ["depthval: " depthval] count: count + 1 faceZ: facenormal/3 ;backface culling (removing back faces) if no-cull [faceZ: negate abs facez ] ;max if faceZ <= 0 [ ; make the depthval the first entry in the block so that the block will be depth sorted by this value temptriangle: copy [] append temptriangle depthval foreach index face [ ; get the vertex pointed to by this index v: pick transformedVertices index append temptriangle reduce ( origin + as-pair v/1 v/2 ) ] ; todo lighting facez: 0.1 - facez either tuple? objColor [ append temptriangle ( objColor * facez ) ;if it's a color ][ facez2: to-integer ( facez * facez * 150) append temptriangle to-image layout/tight compose/deep [image (objColor) effect [luma (facez2)]] ;else it's an image ] append/only result temptriangle ] ] result ] r3d-Load-OFF: func [ "Load 3D OFF files, and create the correspective model" file [file!] /local bloc result verts faces numVerts numFaces numEdges temp_bl maxx minn ] [ bloc: read/lines file ; first of all remove empty lines that can be found in old OFF files remove-each value bloc [empty? value] if not found? find bloc/1 "OFF" [Alert "Block is not an OFF file" return none exit] fileType: bloc/1 bloc: next bloc ; next line ;process possible header comment lines starting with # while [bloc/1/1 = #"#" ] [bloc: next bloc] ;now we have the number of vertexes numVerts: to-integer first parse bloc/1 none numfaces: to-integer second parse bloc/1 none ;do we need it? numedges: to-integer third parse bloc/1 none result: copy [] verts: copy [] faces: copy [] maxx: 0 minn: 0 loop numVerts [ temp_bl: copy [] bloc: next bloc ; next line line: parse bloc/1 none foreach item line [ temp: to-decimal item if temp > maxx [maxx: temp] if temp < minn [minn: temp] append temp_bl temp ] append/only verts temp_bl ] append/only result verts ; the first part of the model is done loop numfaces [ temp_bl: copy [] bloc: next bloc ; next line line: parse bloc/1 none nv: to-integer line/1 ;number of vertexes to add loop nv [ line: next line ;off models start to counter from zero! append temp_bl ((to-integer line/1) + 1) ] append/only faces temp_bl ] append/only result faces ; the second part of the model is done append result (maxx - minn) ;this give an hit for scaling the model result ] ;---------------------------------------- ; ; Load the base model ; ; ; Note: R3d supports loading the .OFF file format through the r3d-load-off function ; ;How to create base model: ;all model are made of triangles, you must give point and triangles to show ;a model is a block with inside 2 blocks: vertices and faces ;vertices contains vertices coordinates [x y z] ;faces set the triangle to show ;so [ 1 2 3] means to fill a triangle using point 1, 2 and 3 ;THE ORDER OF THE POINTS IN FACE DESCRIPTION IS VERY IMPORTANT ; [1 2 3] IS DIFFERENT THAN [ 3 2 1 ] ;USE faces - anticlockwise winding cube-model: [ ; vertices [ [ 1 1 0 ] ;point 1 [ 1 -1 0 ] ;point 2 [ -1 -1 0 ] ;point 3 and so on... [ -1 1 0 ] [ 1 1 1 ] [ 1 -1 1 ] [ -1 -1 1 ] [ -1 1 1 ] ] ; faces - anticlockwise winding [ [ 1 5 6 ] [ 1 6 2 ] [ 2 6 7 ] [ 2 7 3 ] [ 3 7 8 ] [ 3 8 4 ] [ 4 8 5 ] [ 4 5 1 ] [ 8 7 6 ] [ 8 6 5 ] [ 1 2 3 ] [ 1 3 4 ] ] ] cube2-model: [ ; vertices [ [ 0 0 0 ] ;point 1 [ 1 0 0 ] ;point 2 [ 1 1 0 ] ;point 3 and so on... [ 0 1 0 ] [ 0 0 1 ] ;point 5 [ 1 0 1 ] ;point 6 [ 1 1 1 ] ;point 7 and so on... [ 0 1 1 ] ] ; faces - anticlockwise winding [ [ 4 3 2 1] ;just six face square, instead of 12 tirangles [ 5 6 7 8 ] [ 1 5 8 4 ] [ 1 2 6 5] [ 2 3 7 6 ] [ 8 7 3 4 ] ] ] pyramid-model: [ ;vertices [ [1 0.87 2] ;1 [0 0 0] ;2 [2 0 0] ;3 [1 1.73 0] ;4 ] ; faces [ [4 2 1] [4 3 2] [1 3 4] [ 1 2 3] ] ] square-pyramid-model: [ ;vertices [ [1 1 2] ;1 [0 0 0] ;2 [2 0 0] ;3 [2 2 0] ;4 [0 2 0] ; 5 ] ; faces [ [1 5 2 ] [1 2 3] [1 3 4] [1 4 5] [3 2 5] [3 5 4] ] ] wall-model: [ ;vertices [ [0 0 0] ;1 [1 0 0] ;2 [1 0 1 ] ;3 [0 0 1 ] ;4 ] ; faces [ [3 4 1 2 ] ] ] prysm-8-model: [ ;vertices [ [-0.414 -1 0] ;1 [0.414 -1 0] ;2 [1 -0.414 0 ] ;3 [1 0.414 0 ] ;4 [0.414 1 0] ;5 [-0.414 1 0] ;6 [-1 0.414 0 ] ;7 [-1 -0.414 0 ] ;8 [-0.414 -1 1] ;9 [0.414 -1 1] ;10 [1 -0.414 1 ] ;11 [1 0.414 1 ] ;12 [0.414 1 1] ;13 [-0.414 1 1] ;14 [-1 0.414 1 ] ;15 [-1 -0.414 1 ] ;16 ] ; faces [ [8 7 6 5 4 3 2 1 ] [9 10 11 12 13 14 15 16] [1 2 10 9] [2 3 11 10] [3 4 12 11] [4 5 13 12] [5 6 14 13] [6 7 15 14] [7 8 16 15] [8 1 9 16] ] ]
halt ;; to terminate script if DO'ne from webpage
Notes