I've been working on this for a couple of days.
The project shows how to copy a memblock with indices into a new memblock to create 1 object. It also shows how to add your own attribute to a memblock, so you can pass this to the shader.
This particular shader needs access to the vertex numbers, and that is not available in GLSL 1.0, AFAIK. Through this, we can control 1 "tree" at a time, and the other trees will not be changed.
I think this is how one could make grass move as the character walked though it. I am not sure, though.
// Project: 3D Morfing
// Created: 2018-12-24
// show all errors
SetErrorMode(2)
// set window properties
SetWindowTitle( "Crumble" )
SetDisplayAspect( 1366.0/768.0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate(0,0) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
declareGlobals()
type position
x as float
y as float
z as float
endtype
type VertexData
Tree as integer
CollisionBox
StartVertexNumber
EndVertexNumber
VertexInfo as VertexPosition[]
endtype
type VertexPosition
x as float
y as float
z as float
VertexNumber
endtype
createground()
setupcamerapos(3,3,-40)
NumberofTrees = 108
TotalTrees = NumberofTrees
id = treemeshcombiner(NumberofTrees)
//fallingtreephysics()
//id = GetMeshData()
//SetShadowMappingMode( 3 )
//SetShadowSmoothing( 2 )
//Setshadowrange( 30.0 )
//SetShadowMapSize( 2048, 2048 )
//SetShadowBias( 0.0012 )
do
print( "Use W, S, A, D to move and rotate camera." )
print("W - forward, S - backward A, D to turn." )
print( "Click base of tree to activate shake.")
CheckShakeTimer()
MouseOverObject()
movecamera()
print( ScreenFPS() )
//Step3DPhysicsWorld()
Sync()
loop
function declareGlobals()
global treeshader
SetGenerateMipmaps( 1 )
global tree_img
tree_img = LoadImage( "pinetreemobile.png" )
SetGenerateMipmaps( 0 )
global global_treeMB
global tree
treeshader = LoadShader( "treetest.vs", "treetest.ps" )
global usetreeshader = 1
dim CombinedMeshVertexInfo[] as VertexData
global ShakeTimerReset# = .75 // how long to the tree shakes
global ShakeTimer
global TotalTreeVertices
global TotalTrees
global ActiveTree = 1
global StartTreeShake = 0
endfunction
function setupcamerapos(Lookat,Y#,Z#)
SetCameraPosition( 1, 0, Y#, Z# )
SetCameraLookAt( 1, 0, Lookat, 0, 0 )
endfunction
function createground()
global grd
grd = CreateObjectPlane(60,60)
SetObjectRotation( grd, 90, 0, 0 )
img = LoadImage( "baredirt.png" )
SetImageWrapU(img, 1 )
SetImageWrapV(img, 1 )
SetObjectImage( grd, img, 0 )
SetObjectUVScale( grd, 0, 5, 5 )
SetObjectReceiveShadow( grd, 1 )
SetObjectCastShadow( grd,0 )
SetObjectCollisionMode( grd,0 )
endfunction
function _creatememblock(objs, memID)
NumVertices = GetMemblockInt(memID,0)*objs
NumIndices = GetMemblockInt(memID,4)*objs
NumAttributes = GetMemblockInt(memID,8)+1
VertexSize = GetMemblockInt(memID,12)+4 // Position, UV and Normal 12+12+8
// AttributesSize = 36
VertexOffset = GetMemblockInt(memID,16)+16
IndexOffset = VertexOffset + (NumVertices*VertexSize)
global TotalSize
TotalSize = IndexOffset + (NumIndices*4)
id = CreateMemblock(TotalSize)
// Set the values
SetMemblockInt(id,0,NumVertices)
SetMemblockInt(id,4,NumIndices)
SetMemblockInt(id,8,NumAttributes)
SetMemblockInt(id,12,VertexSize)
SetMemblockInt(id,16,VertexOffset)
SetMemblockInt(id,20,IndexOffset)
// Set the attribute Data
// VERTEX ATTRIBUTES
SetMemblockByte( id, 24, 0 ) // float
SetMemblockByte( id, 25, 3 ) // component count
SetMemblockByte( id, 26, 0 ) // normalize
SetMemblockByte( id, 27, 12 ) // string length
SetMemblockString( id, 28, "position" ) // 12 bytes
// NORMALS ATTRIBUTES
SetMemblockByte( id, 40, 0 ) // float
SetMemblockByte( id, 41, 3 ) // component count
SetMemblockByte( id, 42, 0 ) // normalize
SetMemblockByte( id, 43, 8 ) // string length
SetMemblockString( id, 44, "normal" )
// UV ATTRIBUTES
SetMemblockByte( id, 52, 0 ) // float
SetMemblockByte( id, 53, 2 ) // component count
SetMemblockByte( id, 54, 0 ) // normalize
SetMemblockByte( id, 55, 4 ) // string length
SetMemblockString( id, 56, "uv" )
// VertexID ATTRIBUTE
SetMemblockByte( id, 60, 0 ) // float
SetMemblockByte( id, 61, 1 ) // component count
SetMemblockByte( id, 62, 0 ) // normalize
SetMemblockByte( id, 63, 12 ) // string length
SetMemblockString( id, 64, "VertexID" )
endfunction id
function AddVertexDataToMemblock( id as integer,treeMB, NumberofTrees )
//offset = GetMemblockInt(temp_treeMB,16)+(GetMemblockInt(temp_treeMB,12)*vertexNum)
V as VertexPosition
TotalSize = GetMemblockSize( id )
TotalSize_temp = GetMemblockSize( treeMB )
IndexBytes = GetMemblockInt(treeMB,4)*4
CurrentVertex = 0
NumIndices = GetMemblockInt(treeMB,4)
numvert = GetMemblockInt(treeMB,0)
y# = GetObjectraycastY( 0 )-.01
for i = 0 to NumberofTrees-1
z# = treePosArray[i].z
x# = treePosArray[i].x
for vert = 0 to numvert-1
offset = GetMemblockInt(id,16) + (GetMemblockInt(id,12)*CurrentVertex)
temp_offset = GetMemblockInt(treeMB,16) + (GetMemblockInt(treeMB,12)*vert)
vx# = GetMeshMemblockVertexX( treeMB, vert )+x#
vy# = GetMeshMemblockVertexY( treeMB, vert )+y#
vz# = GetMeshMemblockVertexZ( treeMB, vert )+z#
vxn# = GetMeshMemblockVertexNormalX( treeMB, vert )
vyn# = GetMeshMemblockVertexNormalY( treeMB, vert )
vzn# = GetMeshMemblockVertexNormalZ( treeMB, vert )
vu# = GetMeshMemblockVertexU( treeMB, vert )
vv# = GetMeshMemblockVertexV( treeMB, vert )
SetMeshMemblockVertexPosition( id, CurrentVertex, vx#, vy#, vz# )
SetMeshMemblockVertexNormal( id, CurrentVertex, vxn#, vyn#, vzn# )
SetMeshMemblockVertexUV( id, CurrentVertex, vu#, vv# )
SetMemblockFloat( id, offset+32, CurrentVertex ) // set VertexID attribute
V.VertexNumber = CurrentVertex
V.x = vx#
V.y = vy#
V.z = vz#
CombinedMeshVertexInfo[i].VertexInfo.insert(V)
CurrentVertex = CurrentVertex+1
next vert
StartofIndexByte_NewMB = TotalSize-(IndexBytes*(NumberofTrees-i) )
for index = 0 to NumIndices-1
IndexOffset_tempMB = GetMemblockInt(treeMB,20)+(index*4)
IndexOffsetNewMB = StartofIndexByte_NewMB+(index*4)
IndexVert = GetMemblockInt(treeMB,IndexOffset_tempMB)
SetMemblockInt( id, IndexOffsetNewMB, IndexVert+(i*numvert) )
next index
next i
endfunction
function treemeshcombiner(NumberofTrees)
D as VertexData
tree = LoadObject( "treemobile.fbx" )
//tree = LoadObjectWithChildren( "TreeAnim.fbx" )
//SetObjectScalePermanent( obj, .8,.8,.8 )
temp_treeMB = CreateMemblockFromObjectMesh( tree, 1 )
DeleteObject( tree )
for i = 0 to NumberofTrees-1
D.Tree = i+1
D.StartVertexNumber = 0+(i*( GetMemblockInt(temp_treeMB,0)) )
D.EndVertexNumber = D.StartVertexNumber+GetMemblockInt(temp_treeMB,0)-1
CombinedMeshVertexInfo.insert(D)
next i
global_treeMB = _creatememblock(NumberofTrees, temp_treeMB)
dim treePosArray[NumberofTrees-1] as position
setuptreePos()
AddVertexDataToMemblock( global_treeMB,temp_treeMB,NumberofTrees )
DeleteMemblock( temp_treeMB )
tree = CreateObjectFromMeshMemblock( global_treeMB )
//SetObjectMeshFromMemblock(tree, 1, global_treeMB )
TotalTreeVertices = GetMemblockInt(global_treeMB,0)
SetObjectImage( tree, tree_img, 0 )
//SetObjectImage( tree, tree_img_n, 1 )
SetObjectAlphaMask( tree, 1 )
SetObjectReceiveShadow( tree, 1 )
SetObjectCastShadow( tree, 1 )
SetObjectCullMode( tree, 0 )
if usetreeshader = 1 then Usetreeshader()
SetObjectCollisionMode( tree, 0 )
endfunction global_treeMB
function setupTreePos() //setup collision boxes and tree positions
for i = 0 to treePosArray.length
if i <= 12
x = -16
z = 15
treePosArray[i].x = x+(i*3.0)
treePosArray[i].z = z
elseif i <= 24
x = -16
z = 12
treePosArray[i].x = x+((i-12)*2.85)
treePosArray[i].z = z
elseif i <= 36
x = -17
z = 9
treePosArray[i].x = x+((i-24)*2.85)
treePosArray[i].z = z
elseif i <= 48
x = -17
z = 3
treePosArray[i].x = x+((i-36)*2.85)
treePosArray[i].z = z
elseif i <= 60
x = -14
z = 0
treePosArray[i].x = x+((i-48)*2.5)
treePosArray[i].z = z
elseif i <= 72
x = -12
z = -3
treePosArray[i].x = x+((i-60)*2.25)
treePosArray[i].z = z
elseif i <= 84
x = -11
z = -6
treePosArray[i].x = x+((i-72)*2.0)
treePosArray[i].z = z
elseif i <= 96
x = -11
z = -9
treePosArray[i].x = x+((i-84)*1.8)
treePosArray[i].z = z
elseif i <= 108
x = -10
z = -12
treePosArray[i].x = x+((i-96)*1.6)
treePosArray[i].z = z
endif
next i
for i = 0 to treePosArray.length // set up collision boxes
CombinedMeshVertexInfo[i].CollisionBox = CreateObjectBox( .5, .5, .5 )
SetObjectPosition( CombinedMeshVertexInfo[i].CollisionBox, treePosArray[i].x, 0, treePosArray[i].z )
SetObjectVisible( CombinedMeshVertexInfo[i].CollisionBox, 0 )
next i
endfunction
function GetMeshData()
tree = LoadObject( "treemobile.fbx" )
SetObjectImage( tree, tree_img, 0 )
mb = CreateMemblockFromObjectMesh( tree,1 )
SetObjectAlphaMask( tree, 1 )
endfunction mb
function printmemblockinfo(id)
print( "Verts: " +str(GetMemblockInt(id,0)) ) //NumVertices
print( "Indices: " +str(GetMemblockInt(id,4))) //NumIndices
print( "Attributes: " +str(GetMemblockInt(id,8)) ) //NumAttributes 5
print( "VertexSize: " +str(GetMemblockInt(id,12)) ) //VertexSize
print( "VertexOffset: " +str(GetMemblockInt(id,16)) ) //VertexOffset
print( "Index Offset: " +str(GetMemblockInt(id,20)) ) //IndexOffset
print( GetMemblockSize(id ) )
endfunction
function GetMemblockVertexRange(CollisionBox)
index = -1
for i = 0 to CombinedMeshVertexInfo.length
if CombinedMeshVertexInfo[i].CollisionBox = CollisionBox
index = i
exitfunction index
endif
next i
endfunction index
function usetreeshader()
SetObjectShader( tree, treeshader )
SetShaderConstantByName( treeshader, "shakespeed", 40.0,0,0,0 )
SetShaderConstantByName( treeshader, "vertexrange", 300000,300000,0,0 ) // have to set very high to not trigger the shake
endfunction
function CheckShakeTimer()
if timer() - ShakeTimer >= ShakeTimerReset# and StartTreeShake = 1
SetShaderConstantByName( treeshader, "vertexrange", 500000,500000,0,0 ) // make sure the number is high enough to not include any verts
StartTreeShake = 0
endif
endfunction
function movecamera()
if GetRawKeyState( 87 )
MoveCameraLocalZ(1,.2)
elseif GetRawKeyState( 83 )
MoveCameraLocalZ(1,-.2)
elseif GetRawKeyState( 65 )
RotateCameraLocalY(1,-.5)
elseif GetRawKeyState( 68 )
RotateCameraLocalY(1,.5)
endif
endfunction
function MouseOverObject()
myX# = GetPointerX()
myY# = GetPointerY()
my3dX# = Get3DVectorXFromScreen(myX#, myY#)
my3dY# = Get3DVectorYFromScreen(myX#, myY#)
my3dZ# = Get3DVectorZFromScreen(myX#, myY#)
raystartX# = my3dX# + getcameraX(1)
raystartY# = my3dY# + getcameraY(1)
raystartZ# = my3dZ# + getcameraZ(1)
rayEndX# = 1000 * my3dX# + getcameraX(1)
rayEndY# = 1000 * my3dY# + getcameraY(1)
rayEndZ# = 1000 * my3dZ# + getcameraZ(1)
mouse_over_hit = ObjectRayCast( 0, raystartX#, raystartY#, raystartZ#, rayEndX#, rayEndY#, rayEndZ# )
if mouse_over_hit > 0 and GetPointerReleased() = 1
i = GetMemblockVertexRange(mouse_over_hit)
if i > -1
SetShaderConstantByName( treeshader, "vertexrange", CombinedMeshVertexInfo[i].StartVertexNumber,CombinedMeshVertexInfo[i].EndVertexNumber,0,0 )
ShakeTimer = timer()
StartTreeShake = 1
endif
endif
endfunction mouse_over_hit
vs code
attribute highp vec3 position;
attribute mediump vec3 normal;
attribute highp vec2 uv;
attribute float VertexID;
// create attribute denoting VertexID
//uniform to tell which vertices we need
//if statement to move the vertices
uniform highp mat3 agk_WorldNormal;
uniform highp mat4 agk_World;
uniform highp mat4 agk_ViewProj;
varying highp vec3 posVarying;
varying mediump vec3 normalVarying;
varying mediump vec3 lightVarying;
varying highp vec2 uvVarying;
uniform highp vec4 uvBounds0;
uniform float agk_time;
uniform mediump vec3 agk_CameraPos;
uniform float shakespeed;
uniform vec2 vertexrange;
mediump vec3 GetVSLighting( mediump vec3 normal, highp vec3 pos );
void main()
{
uvVarying = uv * uvBounds0.xy + uvBounds0.zw;
highp vec4 pos = agk_World * vec4(position,1.0);
float Vertex = VertexID*1.0;
float wind = .5;
vec3 offset = vec3( (wind*(sin(agk_time*.43)))*.135*clamp(((position.y-.5)/4.5),0.0,1.0), 0, 0 );
pos.x = pos.x + offset.x;
if ( Vertex >= vertexrange.x && Vertex <= vertexrange.y )
{
vec3 offset = vec3( (wind*(sin(agk_time*shakespeed)))*.135*clamp(((position.y-.5)/4.5),0.0,1.0), 0, 0 );
pos.x = pos.x + offset.x;
}
gl_Position = agk_ViewProj * pos;
mediump vec3 norm = normalize(agk_WorldNormal * normal);
posVarying = pos.xyz;
normalVarying = norm;
lightVarying = GetVSLighting( normalVarying, posVarying );
}
PS
varying highp vec2 uvVarying;
uniform sampler2D texture0;
varying mediump vec3 normalVarying;
varying mediump vec3 lightVarying;
varying highp vec3 posVarying;
mediump vec3 GetPSLighting( mediump vec3 normal, highp vec3 pos );
uniform mediump vec4 agk_MeshDiffuse;
uniform mediump vec4 agk_MeshEmissive;
void main()
{
mediump vec4 blendTex = vec4(1.0,1.0,1.0,1.0);
mediump vec3 norm = normalize(normalVarying);
mediump vec3 light = lightVarying + GetPSLighting( norm, posVarying );
mediump vec4 texColor = texture2D(texture0, uvVarying);
gl_FragColor = texColor * blendTex * vec4(light,1.0) * agk_MeshDiffuse + agk_MeshEmissive;
if ( gl_FragColor.a < 0.8 ) discard;
}