Créer un monde "à la minecraft"

Généralités sur la programmation 3D
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Créer un monde "à la minecraft"

Message par kelebrindae »

Bonjour,

(Cela fait un bout de temps que je n'ai rien posté, mais je manque un peu de temps, désolé...)


Voici ma dernière expérimentation: afficher un monde à la façon de Minecraft. (Falsam, ça va te plaire, y'a des cubes partout. :wink: )

ça marche bien, le programme n'affiche que les faces visibles de chaque cube et recrée à la volée celles qui manquent quand on creuse. De plus, le stockage en mémoire est assez économe (2 octets par cube, soit 128Ko pour une matrice de 32x128x32).

Ici, un zip avec programme + textures:
http://keleb.free.fr/codecorner/downloa ... eclone.zip

[UPDATE 22/11/2013]
Grosse mise à jour sur l'affichage: un seul mesh par chunk, et plusieurs chunks à l'écran, créés à la volée quant ils arrivent dans le champ de vision.
Mais j'ai dû désactiver (provisoirement?) les ombres, et je n'ai plus qu'un seul materiau; j'ai pris l'herbe, car elle offre une bonne visibilité. Je remettrai les autres quand j'aurai trouvé comment faire...
N'oubliez pas de récupérer l'include "PerlinNoise" (dans le zip ou plus bas, après le source).

[UPDATE 08/11/2013]
- Grosse optimisation de la création / mise à jour d'un chunk, ce qui permet d'avoir des terrains plus grands.
- Ajout de plusieurs chunk autour du chunk où se trouve le joueur, afin d'agrandir encore le terrain visible. Ces chunks sont créés / détruits à la volée quand il entrent / sortent du champ de vision. Mais multiplier les groupes de géométrie statiques a un impact assez lourd sur les perfs (plus que d'augmenter le nombre de polygones) :|.
(Récupérez bien le zip, car il y a des nouvelles textures).

[UPDATE 06/11/2013]
Ajout des collisions et du saut; ça commence à être sympa! :mrgreen:

[UPDATE 05/11/2013]
Je me heurte cependant à un problème: ça rame. En affichant à peine 14000 polygones, je tombe tout de suite à 30-35 FPS. Pourtant, 14000 polygones, de nos jours, ce n'est vraiment pas la mer à boire... Quelqu'un saurait où est le problème?
Grâce à G-Rom, ça ne rame plus du tout! Tout le décor est passé en géométrie statique et ça booste! :D
Le mouse-picking remarche, si vous êtes assez près de ce que vous visez (pour ne pas dégommer des cubes à l'autre bout du terrain).
La mise à jour du terrain prend un peu longtemps quand vous détruisez un cube, donc je vous conseille de désactiver le debugger.

Prochaine étape: ajouter les collisions avec le décor.

Touches:
- W = affichage fil de fer
- L = Jour / Nuit
- Souris + touches curseur = déplacements

Code:

Code : Tout sélectionner

;************************************************************************************
;  Minecraft clone
;  PB version: 5.21
;  Date: November, 22, 2013
;
;- In game:
;- L  -> Day / night
;- P  -> Display physic bodies
;- W -> Wireframe view
;
;************************************************************************************

; Great Perlin noise generator given by G-Rom!
XIncludeFile "perlinGenerator.pbi"

;************************************************************************************
;-                          ---- Constants and structures ----
;************************************************************************************

; Window size
#SCREENWIDTH = 800
#SCREENHEIGHT = 500

; Camera
#CAMERA = 0
#CAMERASPEED = 0.005
#VIEWING_RANGE = 30

; Sides of a cube
Enumeration
  #TOPSIDE
  #BOTTOMSIDE
  #LEFTSIDE
  #RIGHTSIDE
  #FRONTSIDE
  #BACKSIDE
EndEnumeration

; Size of a chunk (group of cubes that are prepared/built/rendered at the same time)
#CHUNKWIDTH = 15
#CHUNKHEIGHT = 63
#CHUNKLENGTH = 15

; Chunk states
Enumeration
  #CHUNK_NOSTATE
  #CHUNK_PREPARED
  #CHUNK_BUILT
  #CHUNK_RENDERED
EndEnumeration

; Size of the world, in chunks
#WORLDWIDTH = 100
#WORLDHEIGHT = 1  ; only "1" is supported
#WORLDLENGTH = 100



; In a cube, there's six sides, and each one can be either visible or not.
; => 64 possible configurations (%111111)
Global Dim cubeMesh.i(%111111 + 1)
Global Dim faceValue.i(6)  ; Top = %000001, Bottom = %000010, Left = %000100, etc..

; Cube materials (ground, grass, wood, stone, etc..
Structure cubeMat_struct

  mtIntact.i
  mtDamaged.i
  mtAlmostBroken.i
  
  opaque.b
  soundWhenHit.i  ; sound played when left-clicked
  ptLossWhenHit.i ; solidity points lost when left-clicked
EndStructure
Global Dim cubeMat.cubeMat_struct(128) ; 127 cube types maximum (.b dans cube_struct, 0 = empty)

; World infos
Structure cube_struct
  numMat.b
  solidity.b ; 127 = intact, 0 = destroyed
EndStructure
Global boundingBox.i

Structure chunkArray_struct
  
  Array chunk.cube_struct(#CHUNKWIDTH+1,#CHUNKHEIGHT+1,#CHUNKLENGTH+1)
  
  queued.b
  state.b
  
  numMesh.i  
  boundingBoxEntity.i
EndStructure
Global Dim chunkArray.chunkArray_struct(#WORLDWIDTH,#WORLDLENGTH)

Structure toDo_struct
  chunkCoordX.i
  chunkCoordZ.i
  distance.f
EndStructure
Global NewList toDo.toDo_struct()

; Collisions
Structure coords_struct
  x.f
  y.f
  z.f
EndStructure
Global NewList collPointsForZ.coords_struct()
Global NewList collPointsForX.coords_struct()
Global NewList collPointsForFall.coords_struct()
Global NewList collPointsForJump.coords_struct()

; Player
Structure player_struct
  x.f
  y.f
  z.f
  
  xChunk.i
  yChunk.i
  zChunk.i
  
  verticalSpeed.f ; Jumping or falling
  onGround.b ; You can jump only if you're on the ground
EndStructure
Global player.player_struct


; Misc
Global wmain.i
Global mapSprite.i


EnableExplicit


;************************************************************************************
;-                                 ---- Macros ----
;************************************************************************************

; Display FPS in a corner of the screen
Macro DISPLAY_FPS(timer)
    ; Display FPS
    If showFps = #True
      If timer - fpsTimer > 1000
        StartDrawing(SpriteOutput(fpsSprite))
        DrawText(0,0,Str(CountRenderedTriangles()) + " rendered polys, " +  Str(Engine3DFrameRate(#PB_Engine3D_Current))+ " FPS   " )
        DrawText(0,20,"Chunk " + Str(player\xChunk) + "," + Str(player\zChunk) + " : " + Str(player\x) + "," + Str(player\y) + "," + Str(player\z) + "        ")
        StopDrawing()
        fpsTimer = timer
      EndIf
      DisplayTransparentSprite(fpsSprite,0,0) 
    EndIf    
EndMacro

; Check what kind of block is at x,y,z. If there's nothing or the coords are off-limit, result is zero.
Macro GETCELLCONTENT(x,y,z, result)
;   If x < 0 Or x > #CHUNKWIDTH Or y < 0 Or y > #CHUNKHEIGHT Or z < 0 Or z > #CHUNKLENGTH
;     result = 0
;   Else

    result = chunkArray(Int(x/(#CHUNKWIDTH+1)),Int(z/(#CHUNKLENGTH+1)))\chunk(x % (#CHUNKWIDTH+1),y,z % (#CHUNKLENGTH+1))\numMat
;   EndIf
EndMacro


; Check if there's a visible side in the cube at x,y,z
; The result is stored in visibleSides as a six bits field ( %000001, %001010, etc..)
Macro CHECKVISIBLESIDES(xChunk,zChunk,x,y,z,visibleSides)
          ; Which sides of the cube are visible ?
          visibleSides = 0
          
          ; Top          
          If y < #CHUNKHEIGHT And cubeMat(chunkArray(xChunk,zChunk)\chunk(x,(y)+1,z)\numMat)\opaque = 0
            visibleSides | faceValue(#TOPSIDE)
          EndIf
  
          ; Bottom
          If y > 0 And cubeMat(chunkArray(xChunk,zChunk)\chunk(x,(y)-1,z)\numMat)\opaque = 0
            visibleSides | faceValue(#BOTTOMSIDE)
          EndIf
  
          ; Left
          If x = 0 
            If chunkArray(xChunk-1,zChunk)\state >= #CHUNK_PREPARED And cubeMat(chunkArray(xChunk-1,zChunk)\chunk(#CHUNKWIDTH,y,z)\numMat)\opaque = 0
              visibleSides | faceValue(#LEFTSIDE)
            EndIf
          Else
            If cubeMat(chunkArray(xChunk,zChunk)\chunk((x)-1,y,z)\numMat)\opaque = 0
              visibleSides | faceValue(#LEFTSIDE)
            EndIf  
          EndIf
  
          ; Right
          If x = #CHUNKWIDTH
            If chunkArray(xChunk+1,zChunk)\state >= #CHUNK_PREPARED And cubeMat(chunkArray(xChunk+1,zChunk)\chunk(0,y,z)\numMat)\opaque = 0
              visibleSides | faceValue(#RIGHTSIDE)
            EndIf
          Else
            If cubeMat(chunkArray(xChunk,zChunk)\chunk((x)+1,y,z)\numMat)\opaque = 0
              visibleSides | faceValue(#RIGHTSIDE)
            EndIf            
          EndIf
          
          ; Back
          If z = 0 
            If chunkArray(xChunk,zChunk-1)\state >= #CHUNK_PREPARED And cubeMat(chunkArray(xChunk,zChunk-1)\chunk(x,y,#CHUNKLENGTH)\numMat)\opaque = 0
              visibleSides | faceValue(#BACKSIDE)
            EndIf
          Else
            If cubeMat(chunkArray(xChunk,zChunk)\chunk(x,y,(z)-1)\numMat)\opaque = 0
              visibleSides | faceValue(#BACKSIDE)
            EndIf
          EndIf  
          
          ; Front
          If z = #CHUNKLENGTH
            If chunkArray(xChunk,zChunk+1)\state >= #CHUNK_PREPARED And cubeMat(chunkArray(xChunk,zChunk+1)\chunk(x,y,0)\numMat)\opaque = 0
              visibleSides | faceValue(#FRONTSIDE)
            EndIf
          Else
            If cubeMat(chunkArray(xChunk,zChunk)\chunk(x,y,(z)+1)\numMat)\opaque = 0
              visibleSides | faceValue(#FRONTSIDE)
            EndIf         
          EndIf
  
EndMacro

Macro ADDCHUNKTOQUEUE(xChunkAdded,zChunkAdded,newState)
          AddElement(toDo())
          toDo()\chunkCoordX = xChunkAdded
          toDo()\chunkCoordZ = zChunkAdded
          todo()\distance = (xChunkAdded - player\xChunk)*(xChunkAdded - player\xChunk) + (zChunkAdded - player\zChunk)*(zChunkAdded - player\zChunk)
          
          ; Manage closer chunks first
          SortStructuredList(todo(),#PB_Sort_Ascending,OffsetOf(todo_struct\distance),#PB_Float)
          
          chunkArray(xChunkAdded,zChunkAdded)\queued = #True
          chunkArray(xChunkAdded,zChunkAdded)\state = newState
          Debug "Adding to queue: " + Str(toDo()\chunkCoordX) + "," + Str(toDo()\chunkCoordZ) + " state = " + Str(newState)  
EndMacro
       
Macro CHANGECHUNKSTATE(xChunkAdded,zChunkAdded,newState)
  If chunkArray(xChunkAdded,zChunkAdded)\queued = #True
    Debug "Changing state: " + Str(xChunkAdded) + "," + Str(zChunkAdded) + " state " + Str(chunkArray(xChunkAdded,zChunkAdded)\state) + " => " + Str(newState)
    chunkArray(xChunkAdded,zChunkAdded)\state = newState
  Else
    ADDCHUNKTOQUEUE(xChunkAdded,zChunkAdded,newState)
  EndIf
EndMacro

;************************************************************************************
;-                                 ---- Procedures ----
;************************************************************************************

; Initializes the lists of points to check for collision
Procedure initializeCollisionPoints()
  Protected x.f
  
  Restore CollisionPointsForZMove
  Read.f x
  While x <> -999999
    AddElement(collPointsForZ())
    collPointsForZ()\x = x
    Read.f collPointsForZ()\y
    Read.f collPointsForZ()\z
    
    Read.f x
  Wend
  
  Restore CollisionPointsForXMove
  Read.f x
  While x <> -999999
    AddElement(collPointsForX())
    collPointsForX()\x = x
    Read.f collPointsForX()\y
    Read.f collPointsForX()\z
    
    Read.f x
  Wend
  
  Restore CollisionPointsForFalling
  Read.f x
  While x <> -999999
    AddElement(collPointsForFall())
    collPointsForFall()\x = x
    Read.f collPointsForFall()\y
    Read.f collPointsForFall()\z
    
    Read.f x
  Wend
  
  Restore CollisionPointsForJumping
  Read.f x
  While x <> -999999
    AddElement(collPointsForJump())
    collPointsForJump()\x = x
    Read.f collPointsForJump()\y
    Read.f collPointsForJump()\z
    
    Read.f x
  Wend
  
EndProcedure

; Read and initialize the material
Procedure initializeMaterials()
  Protected numMat.i, numtex.i
  Protected matName.s, soundName.s
  
  Restore MaterialDatas
  Read.s matName
  While matName <> "XXX END XXX"
    numMat+1
    ; Texture
    numTex = LoadTexture(#PB_Any,matName)
    cubeMat(numMat)\mtIntact = CreateMaterial(#PB_Any,TextureID(numTex) )
    MaterialFilteringMode(cubeMat(numMat)\mtIntact, #PB_Material_Anisotropic, 6)
    
    ; Sound when hit
    Read.s soundName
    If soundName <> ""
      cubeMat(numMat)\soundWhenHit = LoadSound(#PB_Any,soundName)
    EndIf
    
    ; Opaque or not
    Read.b cubeMat(numMat)\opaque
    If cubeMat(numMat)\opaque = #False
      MaterialBlendingMode(cubeMat(numMat)\mtIntact, #PB_Material_AlphaBlend)
    EndIf
    
    ; "Damaged" materials
    cubeMat(numMat)\mtDamaged = CopyMaterial(cubeMat(numMat)\mtIntact,#PB_Any)
    ScrollMaterial(cubeMat(numMat)\mtDamaged,0,0.25,#PB_Material_Fixed)
    cubeMat(numMat)\mtAlmostBroken = CopyMaterial(cubeMat(numMat)\mtIntact,#PB_Any)
    ScrollMaterial(cubeMat(numMat)\mtAlmostBroken,0,0.50,#PB_Material_Fixed)  
    
    ; Solidity points lost when hit
    Read.i cubeMat(numMat)\ptLossWhenHit
    
    Read.s matName
  Wend  
  
EndProcedure


; Load or Build the needed meshes
Procedure buildCubeMeshes()
  Protected i.i,j.i, k.i
  Protected nbVert.i,nbFaces.i
  Protected x.f,y.f,z.f
  Protected nx.f,ny.f,nz.f
  Protected u.f,v.f
  Protected v1.i,v2.i,v3.i
  Protected vertStart.i
  Protected cubeMesh.i

  ; Create the directory where meshes are stored
  If FileSize("meshes") <> -2
    CreateDirectory("meshes")
  EndIf
  
  ; Associate each face with a bit position in a 6 digits binary number.
  ; Top = %000001, Bottom = %000010, Left = %000100, etc..
  For i=#TOPSIDE To #BACKSIDE
    faceValue(i) = Pow(2,i)
  Next i
  
  ; For each combination, create the mesh.
  For i=%000001 To %111111
    
    ; Load the mesh
    cubeMesh(i) = LoadMesh(#PB_Any,"cube_" + RSet(Bin(i),6,"0") + ".mesh")
    If cubeMesh(i) = 0
    
      ; Create the mesh
      Debug "Mesh " + "cube_" + RSet(Bin(i),6,"0") + ".mesh" + " not found; building it..."
      vertStart = 0
      cubeMesh(i) = CreateMesh(#PB_Any)
      
      For j=#TOPSIDE To #BACKSIDE
        ; If the mesh must not have the current side, skip to the next.
        If i & faceValue(j) = 0
          Continue
        EndIf
        
        ; Get the correct datas
        Select j
          Case #TOPSIDE
            Restore meshTopDatas
          Case #BOTTOMSIDE
            Restore meshBottomDatas
          Case #LEFTSIDE
            Restore meshLeftDatas
          Case #RIGHTSIDE
            Restore meshRightDatas
          Case #FRONTSIDE
            Restore meshFrontDatas
          Case #BACKSIDE
            Restore meshBackDatas
        EndSelect
        
        ; Read number of vertices and triangles
        Read.i nbVert:Read.i nbFaces
        
        ; Add the vertices to mesh
        For k = 1 To nbVert  
          Read.f x:Read.f y:Read.f z
          Read.f nx:Read.f ny:Read.f nz
          Read.f u:Read.f v
          
          MeshVertexPosition(x,y,z)
          MeshVertexNormal(nx,ny,nz)
          MeshVertexTextureCoordinate(u,v)
        Next k
       
        ; Add the triangles to mesh
        For k = 1 To nbFaces
          Read.i v1:Read.i v2:Read.i v3
          MeshFace(v1+vertStart,v2+vertStart,v3+vertStart)
        Next k
        
        
        vertstart + nbVert
      Next j
    
      ; Finalize mesh
      FinishMesh(#True)
      BuildMeshShadowVolume(cubeMesh(i))
      BuildMeshTangents(cubeMesh(i))
  
      SaveMesh(cubeMesh(i),"meshes/cube_" + RSet(Bin(i),6,"0") + ".mesh")
    EndIf
    
;     GetMeshData(cubeMesh(i),0,vertexInfos(),#PB_Mesh_Vertex|#PB_Mesh_UVCoordinate,0,MeshVertexCount(cubeMesh(i))-1 )
;     GetMeshData(cubeMesh(i),0,faceInfos(),#PB_Mesh_Face,0,MeshVertexCount(cubeMesh(i))*1.5 -1 )    
    
;     cubeEntity(i) = CreateEntity(#PB_Any,MeshID(cubeMesh(i)),#PB_Material_None,-999+i,-999,-999)
;     EntityRenderMode(cubeEntity(i),0)
;     HideEntity(cubeEntity(i),#True)
    
  Next i
  
  boundingBox = CreateCube(#PB_Any,1)
EndProcedure


; Initialize a random world
Procedure prepareChunk(xChunk.i,zChunk.i)  
  Protected divW.f = 2 / (#WORLDWIDTH)
  Protected divL.f = 2 / (#WORLDLENGTH)
  Protected noise.d,height.i
  Protected x.i,y.i,z.i, fill.i
  
  For z = 0 To #CHUNKLENGTH
    For x = 0 To #CHUNKWIDTH
      noise  = PERLIN_unsigned(PERLIN_generateNoise2D(divW * (x + xChunk * (#CHUNKWIDTH+1)), divL * (z + zChunk * (#CHUNKLENGTH+1)), 4, 4, 10))
      height = 10 + Int(noise * (#CHUNKHEIGHT-20))
          
      ; Fill what's underground
       For fill = height To 0 Step -1
         chunkArray(xChunk,zChunk)\chunk(x,fill,z)\numMat = 1
       Next fill
  
    Next x
  Next z
  
    
  ; Tree
;   chunkArray(xChunk,zChunk)\chunk(14,21,14)\numMat = 3
;   chunkArray(xChunk,zChunk)\chunk(14,22,14)\numMat = 3
;   chunkArray(xChunk,zChunk)\chunk(14,23,14)\numMat = 3
;   chunkArray(xChunk,zChunk)\chunk(14,24,14)\numMat = 3
;   i=2
;   For y = 0 To i
;     For x = 0 To i
;       For z = 0 To i
;         If chunkArray(xChunk,zChunk)\chunk(13+x,23+y,13+z)\numMat = 0 And Random(100) > 25
;           chunkArray(xChunk,zChunk)\chunk(13+x,23+y,13+z)\numMat = 4
;         EndIf
;       Next z
;     Next x
;   Next y


  ; Post-traitement: les cubes de terre avec rien au-dessus sont en herbe
  For y = 0 To #CHUNKHEIGHT
    For x = 0 To #CHUNKWIDTH
      For z = 0 To #CHUNKLENGTH
        If chunkArray(xChunk,zChunk)\chunk(x,y,z)\numMat > 0
          chunkArray(xChunk,zChunk)\chunk(x,y,z)\solidity = 127
          If chunkArray(xChunk,zChunk)\chunk(x,y,z)\numMat = 1 And y < #CHUNKHEIGHT And chunkArray(xChunk,zChunk)\chunk(x,y+1,z)\numMat = 0
            
            chunkArray(xChunk,zChunk)\chunk(x,y,z)\numMat = 2
            
          EndIf
        EndIf
      Next z
    Next x
  Next y
  
  Debug "Chunk " + Str(xChunk) + "," + Str(zChunk) + " is prepared."
  
  chunkArray(xchunk,zchunk)\state = #CHUNK_PREPARED
  If chunkArray(xchunk-1,zchunk)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xchunk-1,zchunk,#CHUNK_PREPARED)
  EndIf
  If chunkArray(xchunk+1,zchunk)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xchunk+1,zchunk,#CHUNK_PREPARED)
  EndIf
  If chunkArray(xchunk,zchunk-1)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xchunk,zchunk-1,#CHUNK_PREPARED)
  EndIf
  If chunkArray(xchunk,zchunk+1)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xchunk,zchunk+1,#CHUNK_PREPARED)
  EndIf
  
EndProcedure


; Create a mesh with the visibles parts of a chunk
Procedure buildChunk(xChunk.i=2,zChunk.i=2,xOri.i=0,yOri.i=0,zOri.i=0,xEnd.i=#CHUNKWIDTH,yEnd.i=#CHUNKHEIGHT,zEnd.i=#CHUNKLENGTH)
  Protected x.i,y.i,z.i,i.i
  Protected visibleSides.i, mat.i
  Protected index.i
  Protected Dim vertInfos.PB_MeshVertex(0)
  Protected Dim faceInfos.PB_MeshFace(0)
  Protected decalX.f,decalY.f,decalZ.f
  Protected t.i
  
  t = ElapsedMilliseconds()
  
  If chunkArray(xChunk,zChunk)\numMesh <> 0
    FreeMesh(chunkArray(xChunk,zChunk)\numMesh)
  EndIf
  chunkArray(xChunk,zChunk)\numMesh = CreateMesh(#PB_Any)
  
  For y = yOri To yEnd
    For x = xOri To xEnd
      For z = zOri To zEnd
        ; Check if there's anything at x,y,z
            If chunkArray(xChunk,zChunk)\chunk(x,y,z)\numMat > 0
          
          ; List the visible sides at this location
          CHECKVISIBLESIDES(xChunk,zChunk,x,y,z,visibleSides)
          
          ; If there's at least one visible face, create the entity with the correct material
          If visibleSides > 0

            ;If chunk(x,y,z)\solidity <= 64
            ;  mat = MaterialID(cubeMat(chunk(x,y,z)\numMat)\mtAlmostBroken)
            ;ElseIf chunk(x,y,z)\solidity <= 96
            ;  mat = MaterialID(cubeMat(chunk(x,y,z)\numMat)\mtDamaged)
            ;Else
            ;  mat = MaterialID(cubeMat(chunk(x,y,z)\numMat)\mtIntact)
            ;EndIf
            
            ;SetEntityMaterial(cubeEntity(visibleSides),mat)
            
            GetMeshData(cubeMesh(visibleSides),0,vertInfos(),#PB_Mesh_Vertex|#PB_Mesh_Normal|#PB_Mesh_UVCoordinate,0,MeshVertexCount(cubeMesh(visibleSides))-1 )
            GetMeshData(cubeMesh(visibleSides),0,faceInfos(),#PB_Mesh_Face,0,MeshVertexCount(cubeMesh(visibleSides))*1.5 -1 )    
            
            For i = 0 To MeshVertexCount(cubeMesh(visibleSides))-1
              MeshVertexPosition(vertInfos(i)\x+x,vertInfos(i)\y+y,vertInfos(i)\z+z)
              MeshVertexNormal(vertInfos(i)\NormalX,vertInfos(i)\NormalY,vertInfos(i)\NormalZ)
              MeshVertexTangent(vertInfos(i)\TangentX,vertInfos(i)\TangentY,vertInfos(i)\TangentZ)
              MeshVertexTextureCoordinate(vertInfos(i)\u,vertInfos(i)\v)
            Next i
            For i = 0 To MeshVertexCount(cubeMesh(visibleSides))*1.5-1 Step 3
              MeshFace(faceInfos(i)\Index + index,faceInfos(i+1)\Index + index,faceInfos(i+2)\Index + index)
            Next i
            index + MeshVertexCount(cubeMesh(visibleSides))
          EndIf

        EndIf

      Next z
    Next x
  Next y
  FinishMesh(chunkArray(xChunk,zChunk)\numMesh)
  
  t = ElapsedMilliseconds()
  chunkArray(xChunk,zChunk)\state = #CHUNK_BUILT
  Debug "Chunk " + Str(xChunk) + "," + Str(zChunk) + " is built."
  
  decalX = (#CHUNKWIDTH + 1) / 2 + xChunk * (#CHUNKWIDTH + 1)
  decalY =  (#CHUNKHEIGHT + 1) / 2
  decalZ = (#CHUNKLENGTH + 1) / 2 + zChunk * (#CHUNKLENGTH + 1)

  chunkArray(xChunk,zChunk)\boundingBoxEntity = CreateEntity(#PB_Any,MeshID(boundingBox),#PB_Material_None,decalX,decalY,decalZ)
  ScaleEntity(chunkArray(xChunk,zChunk)\boundingBoxEntity,#CHUNKWIDTH+1,#CHUNKHEIGHT+1,#CHUNKLENGTH+1,#PB_Absolute)
  HideEntity(chunkArray(xChunk,zChunk)\boundingBoxEntity,#True)
  

EndProcedure


Procedure displayWorld(xPlayer.i,yPlayer.i,zPlayer.i)
  Protected x.i, z.i
  Protected chunkEntity.i
  Protected found.b
  Protected t.i,t1.i, nbEnt.i
  
  StartDrawing(SpriteOutput(mapSprite))
  Box(0,0,80,80,$000000)
  
  t = ElapsedMilliseconds() 
  CreateStaticGeometry(0, (#CHUNKWIDTH+1)*#WORLDWIDTH, (#CHUNKHEIGHT+1)*#WORLDHEIGHT, (#CHUNKLENGTH+1)*#WORLDLENGTH, #False)
  For z = zPlayer-5 To zPlayer+5
    For x = xPlayer-5 To xPlayer+5
      Box((x-xPlayer+5)*8,(z-zPlayer+5)*8,7,7,$777777)
      
      ; If the chunk is not in the viewing range, skip it
      If (x - xPlayer)*(x - xPlayer) + (z - zPlayer)*(z - zPlayer) > #VIEWING_RANGE
        Continue
      EndIf
      
      ; If it's in the viewing range but not built yet, add it to queue (if not queued already) and skip
      If chunkArray(x,z)\numMesh = 0
        If chunkArray(x,z)\queued = #False
          ADDCHUNKTOQUEUE(x,z,chunkArray(x,z)\state) ; don't change chunk's state
        EndIf
        Continue
      EndIf
      
      ; If the chunk is built and visible, add it to static geometry
      If CheckObjectVisibility(#CAMERA,EntityID(chunkArray(x,z)\boundingBoxEntity))
        Box((x-xPlayer+5)*8,(z-zPlayer+5)*8,7,7,$FFFFFF)
        
        chunkEntity = CreateEntity(#PB_Any,MeshID(chunkArray(x,z)\numMesh),MaterialID(cubeMat(2)\mtIntact))
        AddStaticGeometryEntity(0, EntityID(chunkEntity), x * (#CHUNKWIDTH+1),0,z * (#CHUNKLENGTH+1))
        FreeEntity(chunkEntity)
        If chunkArray(x,z)\state = #CHUNK_BUILT
          chunkArray(x,z)\state = #CHUNK_RENDERED
        EndIf

        nbEnt + 1
        
      Else
        ; Not on screen but previously rendered? => goes back to "built" state
        If chunkArray(x,z)\state = #CHUNK_RENDERED
          chunkArray(x,z)\state = #CHUNK_BUILT
        EndIf
      EndIf
    Next x
  Next z
  
  Box(5*8+1,5*8+1,5,5,$0000FF)
  StopDrawing()
  
  t1 = ElapsedMilliseconds()
  BuildStaticGeometry(0)
  Debug "Building static geometry : " + Str(ElapsedMilliseconds() - t1) + " ms."

  
  If nbEnt > 0
    Debug "----------"
    Debug "Rendered " + Str(nbEnt) + " chunks in " + Str(ElapsedMilliseconds() - t) + " ms."
  EndIf

EndProcedure


Procedure deleteCube(x.i,y.i,z.i)
  Protected i.i
  Protected xOri.i,yOri.i,zOri.i
  Protected xEnd.i,yEnd.i,zEnd.i
  Protected xChunk.i,zChunk.i
  
  ; Delete the block
  xChunk = Int(x / (#CHUNKWIDTH+1))
  zChunk = Int(z / (#CHUNKLENGTH+1))
  x = (x % (#CHUNKWIDTH+1))
  z = (z % (#CHUNKLENGTH+1))
  
  If chunkArray(xChunk,zChunk)\chunk(x,y,z)\solidity =< 0
    chunkArray(xChunk,zChunk)\chunk(x,y,z)\numMat = 0
    
    ; Set the limits of the borders scan
    xOri = x-1
    If xOri < 0: xOri = 0 : EndIf
    yOri = y-1
    If yOri < 0: yOri = 0 : EndIf
    zOri = z-1
    If zOri < 0: zOri = 0 : EndIf
    xEnd = x+1
    If xEnd > #CHUNKWIDTH: xEnd = #CHUNKWIDTH : EndIf
    yEnd = y+1
    If yEnd > #CHUNKHEIGHT: yEnd = #CHUNKHEIGHT : EndIf
    zEnd = z+1
    If zEnd > #CHUNKLENGTH: zEnd = #CHUNKLENGTH : EndIf
  Else
    ; The block is still here; no need to redraw the surroundings.
    xOri = x: yOri=y: zOri = z
    xEnd = x: yEnd=y: zEnd = z
  EndIf
  
  
  ; Update the display with the changes we've just made.
  ;displayWorld(xOri,yOri,zOri,xEnd,yEnd,zEnd)  ; redraw only what's necessary
  
  If x = 0 And chunkArray(xChunk-1,zChunk)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xChunk-1,zChunk,#CHUNK_PREPARED)
  EndIf
  If x = #CHUNKWIDTH And chunkArray(xChunk+1,zChunk)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xChunk+1,zChunk,#CHUNK_PREPARED)
  EndIf
  If z = 0 And chunkArray(xChunk,zChunk-1)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xChunk,zChunk-1,#CHUNK_PREPARED)
  EndIf
  If z = #CHUNKLENGTH And chunkArray(xChunk,zChunk+1)\state >= #CHUNK_BUILT
    CHANGECHUNKSTATE(xChunk,zChunk+1,#CHUNK_PREPARED)
  EndIf
  CHANGECHUNKSTATE(xChunk,zChunk,#CHUNK_PREPARED) ; chunk is already prepared. Now rebuild and render it.
  
EndProcedure


;************************************************************************************
;-                                 ---- Game Procedure ----
;************************************************************************************

Procedure playGame()
  Protected wireframe.b, viewBodies.b, dontQuit.b = #True, eventId.i
  Protected mousePick.b, mouseX.f, mouseY.f, mouseClickable.b, visibleSides.i
  Protected xPick.i,yPick.i,zPick.i, result.b
  Protected xdir.f,ydir.f,zdir.f
  Protected fpsSprite.i, fpsTimer.i, showFps.b = #True
  Protected timer.i, oldTimer.i, loopTime.f
  Protected cursorMesh.i, mtCursor.i, cursor.i
  Protected isDayTime.b, i.i,j.i
  Protected oldxChunk.i, oldzChunk.i
  Protected oldx.f,oldy.f,oldz.f,newX.f,newY.f,newZ.f
  Protected xColl.i,yColl.i,zColl.i
  Protected oldTimerRefresh.i
  Protected mustRedraw.b
  
  ;- Sprite used to display FPS
  fpsSprite = CreateSprite(#PB_Any,640,38)
  mapSprite = CreateSprite(#PB_Any,90,90)
  
  ;- Camera
  CreateCamera(#CAMERA, 0, 0, 100, 100)
  CameraBackColor(#CAMERA,$000000)
  ;CameraFOV(#CAMERA,60)

  
  ;-Light
  CreateLight(1,$FFFFFF,CameraX(#CAMERA),CameraY(#CAMERA),CameraZ(#CAMERA))
  LightAttenuation(1,20,0)
  WorldShadows(#PB_Shadow_Additive)
  Fog($000000,50,10,30)
  
  ;- 3D cursor
  cursorMesh = CreateCube(#PB_Any,1.1)
  mtCursor = CreateMaterial(#PB_Any,TextureID(LoadTexture(#PB_Any,"cursor.png")))
  DisableMaterialLighting(mtCursor,#True)
  MaterialShadingMode(mtCursor,#PB_Material_Flat|#PB_Material_Wireframe)
  ScrollMaterial(mtCursor, 1, 1, #PB_Material_Animated)
  cursor = CreateEntity(#PB_Any,MeshID(cursorMesh),MaterialID(mtCursor),16,22,16,$00010)
  EntityRenderMode(cursor,0)
  DisableEntityBody(cursor,#True)
  
  ;- World
  RandomSeed(123456789) ; Change this to change the world !
  player\x = 328 : player\y = 60 : player\z = 328
  player\xChunk = Int(player\x/(#CHUNKWIDTH+1))
  player\zChunk = Int(player\z/(#CHUNKLENGTH+1))
  oldxChunk = player\xChunk
  oldzChunk = player\zChunk
    
  ADDCHUNKTOQUEUE(player\xChunk,player\zChunk,#CHUNK_NOSTATE)
  
  ;- Main loop
  oldTimer = ElapsedMilliseconds()
  KeyboardMode(#PB_Keyboard_International)
  Repeat
    Delay(1)
    
    ; Windows events
    eventID = WindowEvent()
    While eventID <> 0 And dontQuit = #True
      eventID = WindowEvent()
    Wend
    ExamineKeyboard()
    
    
    ;- Function keys (W, P, L)
    ; Activate wireframe render
    If KeyboardReleased(#PB_Key_W)
      wireframe = 1-wireframe
      If wireframe = #True
        CameraRenderMode(0,#PB_Camera_Wireframe)
      Else
        CameraRenderMode(0,#PB_Camera_Textured)
      EndIf
    EndIf
    
    ; Activate physics render
    If KeyboardReleased(#PB_Key_P)
      viewBodies = 1-viewBodies
      If viewBodies  = #True
        WorldDebug(#PB_World_DebugBody)
      Else
        WorldDebug(#PB_World_DebugNone)
      EndIf
    EndIf
    
    ; Change day / night
    If KeyboardReleased(#PB_Key_L)
      isDayTime = 1 - isDayTime
      If isDayTime = #True
        AmbientColor($777777)
        CreateLight(0,$FFFFFF,2000,4000,3000)
        CameraBackColor(#CAMERA,$D9A789)
        LightAttenuation(1,10,1)
        Fog($C2AF80,20,10,100)
      Else
        FreeLight(0)
        CameraBackColor(#CAMERA,$000000)
        LightAttenuation(1,20,0)
        Fog($000000,50,10,30)
      EndIf
    EndIf
    
    If KeyboardReleased(#PB_Key_R)
      mustRedraw = #True
    EndIf
    
    
    ;- Movements (mouse + arrows)
    timer = ElapsedMilliseconds()
    loopTime = 1 + (timer - oldtimer)
    If looptime > 32
      looptime = 32
    EndIf
    oldtimer = timer
    
    ; Look around    
    If ExamineMouse()
      MouseX = -(MouseDeltaX()/20)
      MouseY = -(MouseDeltaY()/20)
      
      If MouseX <> 0
        For j = player\zChunk - 5 To player\zChunk + 5
          For i = player\xChunk -5 To player\xChunk +5
            If (i - player\xChunk )*(i - player\xChunk ) + (j - player\zChunk )*(j - player\zChunk ) <= #VIEWING_RANGE
              If chunkArray(i,j)\state = #CHUNK_BUILT And CheckObjectVisibility(#CAMERA,EntityID(chunkArray(i,j)\boundingBoxEntity)) 
                mustRedraw = #True
                Break 2
              EndIf
            EndIf
          Next i
        Next j
        
      EndIf
      
      RotateCamera(#CAMERA, MouseY, MouseX, 0, #PB_Relative)
    EndIf   
    xdir = CameraDirectionX(#CAMERA)
    ydir = CameraDirectionY(#CAMERA)
    zdir = CameraDirectionZ(#CAMERA)
    
    oldx = player\x : oldy = player\y : oldz = player\z
    newX = oldX : newY = oldY : newZ = oldZ

    ; Walk
    If KeyboardPushed(#PB_Key_Up)
      newX + xDir * (#CAMERASPEED * loopTime)
      newZ + zDir * (#CAMERASPEED * loopTime)
    EndIf
    If KeyboardPushed(#PB_Key_Down)
      newX - xDir * (#CAMERASPEED * loopTime)
      newZ - zDir * (#CAMERASPEED * loopTime)      
    EndIf
    
    ; Strafe
    If KeyboardPushed(#PB_Key_Left)
      newX + (xDir*Cos(Radian(90)) + zDir*Sin(Radian(90))) * (#CAMERASPEED * loopTime)
      newZ + (-xDir*Sin(Radian(90)) + zDir*Cos(Radian(90))) * (#CAMERASPEED * loopTime)      
    EndIf
    If KeyboardPushed(#PB_Key_Right)
      newX - (xDir*Cos(Radian(90)) + zDir*Sin(Radian(90))) * (#CAMERASPEED * loopTime)
      newZ - (-xDir*Sin(Radian(90)) + zDir*Cos(Radian(90))) * (#CAMERASPEED * loopTime)      
    EndIf
    
    ; Jump
    If KeyboardPushed(#PB_Key_Space) And player\onGround = #True
      player\verticalSpeed = 0.015     
    EndIf    
    
    ;- Collisions
    If newZ <> oldZ
      ForEach collPointsForZ()
        xColl = oldX + collPointsForZ()\x
        yColl = oldY + collPointsForZ()\y
        zColl = newZ + collPointsForZ()\z
        GETCELLCONTENT(xColl,yColl,zColl, result)
        If result > 0
          newZ = oldZ
          Break
        EndIf
      Next collPointsForZ()
      player\zChunk = Int(player\z / (#CHUNKLENGTH+1))
      If oldzChunk <> player\zChunk
        mustRedraw = #True
        oldzChunk = player\zChunk
      EndIf
    EndIf
    
    If newX <> oldX
      ForEach collPointsForX()
        xColl = newX + collPointsForX()\x
        yColl = oldY + collPointsForX()\y
        zColl = oldZ + collPointsForX()\z
        GETCELLCONTENT(xColl,yColl,zColl, result)
        If result > 0
          newX = oldX
          Break
        EndIf
      Next collPointsForX()
      player\xChunk = Int(player\x / (#CHUNKWIDTH+1))
      If oldxChunk <> player\xChunk
        mustRedraw = #True
        oldxChunk = player\xChunk
      EndIf
    EndIf
    
    ; Gravity
    If player\verticalSpeed > -0.02
      player\verticalSpeed - (0.000055 * loopTime)
    EndIf
    newY + (player\verticalSpeed * loopTime)
    player\onGround = #False
    
    If player\verticalSpeed < 0
      ForEach collPointsForFall()
        xColl = oldX + collPointsForFall()\x
        yColl = newY + collPointsForFall()\y
        zColl = oldZ + collPointsForFall()\z
        GETCELLCONTENT(xColl,yColl,zColl, result)
        If result > 0
          newY = oldY
          player\verticalSpeed = 0
          player\onGround = #True
          Break
        EndIf
      Next collPointsForFall()
    Else
      ForEach collPointsForJump()
        xColl = oldX + collPointsForJump()\x
        yColl = newY + collPointsForJump()\y
        zColl = oldZ + collPointsForJump()\z
        GETCELLCONTENT(xColl,yColl,zColl, result)
        If result > 0
          newY = oldY
          player\verticalSpeed = 0
          Break
        EndIf
      Next collPointsForJump()
    EndIf

    ; Now, we can place the player and the camera at their new position
    player\x = newX : player\y = newY : player\z = newZ
    MoveCamera(#CAMERA,player\x - xDir/2,player\y + 1.7 - yDir/2,player\z - zdir/2,#PB_Absolute)
    MoveLight(1,player\x,player\y + 1.5,player\z,#PB_Absolute)
    
    
    ;- Left button click
    ; Get camera's direction and check if there's something in this direction
    mousePick = #False
    For i = 1 To 4
      xPick = player\x  + xdir * i
      yPick = player\y + 1.7 + ydir * i
      zPick = player\z  + zdir * i
      
      ; Boundary check
      GETCELLCONTENT(xPick,yPick,zPick, result)
      
      ; The search is over as soon as we find something visible
      If result > 0
        CHECKVISIBLESIDES( Int(xPick/(#CHUNKWIDTH+1)),Int(zPick/(#CHUNKLENGTH+1)),(xPick % (#CHUNKWIDTH+1)),yPick,(zPick % (#CHUNKLENGTH+1)), visibleSides)        
        If visibleSides <> 0
          mousePick = #True
          Break
        EndIf
      EndIf
    Next i
    
    ; If the mousepick was succesfull, allow the player to dig.
    If mousePick = #True
      HideEntity(cursor,#False)
      MoveEntity(cursor,xPick,yPick,zPick, #PB_Absolute)
      
      If MouseButton(#PB_MouseButton_Left) <> 0
        If mouseClickable = #True
          mouseClickable = #False
          
          If cubeMat(chunkArray(Int(xPick / (#CHUNKWIDTH+1)),Int(zPick / (#CHUNKLENGTH+1)))\chunk((xPick % (#CHUNKWIDTH+1)),yPick,(zPick % (#CHUNKLENGTH+1)))\numMat)\soundWhenHit <> 0
            PlaySound(cubeMat(chunkArray(Int(xPick / (#CHUNKWIDTH+1)),Int(zPick / (#CHUNKLENGTH+1)))\chunk((xPick % (#CHUNKWIDTH+1)),yPick,(zPick % (#CHUNKLENGTH+1)))\numMat)\soundWhenHit,#PB_Sound_MultiChannel)
          EndIf
          
          chunkArray(Int(xPick / (#CHUNKWIDTH+1)),Int(zPick / (#CHUNKLENGTH+1)))\chunk((xPick % (#CHUNKWIDTH+1)),yPick,(zPick % (#CHUNKLENGTH+1)))\solidity - cubeMat(chunkArray(Int(xPick / (#CHUNKWIDTH+1)),Int(zPick / (#CHUNKLENGTH+1)))\chunk((xPick % (#CHUNKWIDTH+1)),yPick,(zPick % (#CHUNKLENGTH+1)))\numMat)\ptLossWhenHit
          deleteCube(xPick,yPick,zPick)
        EndIf
      Else
        mouseClickable = #True
      EndIf
  
    Else
      HideEntity(cursor,#True)
    EndIf
    
    ;- Prepare the display
    If ListSize(toDo()) > 0
      FirstElement(toDo())
      ;Debug Str(toDo()\chunkCoordX) + "," + Str(toDo()\chunkCoordZ) + ":" + Str(chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\prepared) + " | " + Str(chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\built) + " | " + Str(chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\rendered) 
      If chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\state < #CHUNK_PREPARED
        prepareChunk(toDo()\chunkCoordX,toDo()\chunkCoordZ)
      ElseIf chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\state < #CHUNK_BUILT
        buildChunk(toDo()\chunkCoordX,toDo()\chunkCoordZ)
      ElseIf chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\state = #CHUNK_BUILT
        chunkArray(toDo()\chunkCoordX,toDo()\chunkCoordZ)\queued = #False
 
        DeleteElement(toDo())
        mustRedraw = #True
      EndIf
    EndIf
    
    If mustRedraw = #True
      displayWorld(player\xChunk,0,player\zChunk)
      mustRedraw = #False
    EndIf


    
    ;- Render
    RenderWorld()
    
    ;- Display FPS
    DISPLAY_FPS(timer)
    DisplayTransparentSprite(mapSprite,700,0)
    
    FlipBuffers()
  
  Until KeyboardPushed(#PB_Key_Escape) Or dontQuit = #False
  
  
  FreeCamera(#PB_All)
  FreeLight(#PB_All)
  
EndProcedure


;************************************************************************************
;-                                 ---- Main program ----
;************************************************************************************

;- Initialization
InitEngine3D()
InitSprite()
InitKeyboard()
InitMouse()
UsePNGImageDecoder()

Add3DArchive("textures",#PB_3DArchive_FileSystem)
Add3DArchive("meshes",#PB_3DArchive_FileSystem)


;- Window
OpenWindow(Wmain,0, 0, #SCREENWIDTH,#SCREENHEIGHT ,"Cube environnement",#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(Wmain), 0, 0, #SCREENWIDTH,#SCREENHEIGHT, 0, 0, 0,#PB_Screen_NoSynchronization)


;-Materials
initializeMaterials()

;- Meshes
buildCubeMeshes()

;- Prepare collision checks
initializeCollisionPoints()

;- Game
playGame()


End


;************************************************************************************
;-                                 ---- Datas ----
;************************************************************************************
;- Meshes datas
DataSection
  meshTopDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.5,0.5,-0.5
  Data.f 0,1,0
  Data.f 0,0
  Data.f 0.5,0.5,-0.5
  Data.f 0,1,0
  Data.f 0.25,0
  Data.f 0.5,0.5,0.5
  Data.f 0,1,0
  Data.f 0.25,0.25
  Data.f -0.5,0.5,0.5
  Data.f 0,1,0
  Data.f 0,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2
  
MeshBottomDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.50,-0.50,0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.50,0.00
  Data.f 0.50,-0.50,0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.75,0.00
  Data.f 0.50,-0.50,-0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.75,0.25
  Data.f -0.50,-0.50,-0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.50,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshLeftDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.50,0.50,-0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.25,0.00
  Data.f -0.50,0.50,0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.50,0.00
  Data.f -0.50,-0.50,0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.50,0.25
  Data.f -0.50,-0.50,-0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshRightDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f 0.50,0.50,0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.25,0.00
  Data.f 0.50,0.50,-0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.50,0.00
  Data.f 0.50,-0.50,-0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.50,0.25
  Data.f 0.50,-0.50,0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshFrontDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.50,0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.25,0.00
  Data.f 0.50,0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.50,0.00
  Data.f 0.50,-0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.50,0.25
  Data.f -0.50,-0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshBackDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f 0.50,0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.25,0.00
  Data.f -0.50,0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.50,0.00
  Data.f -0.50,-0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.50,0.25
  Data.f 0.50,-0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2
  
;- Materials datas
MaterialDatas:
Data.s "ground.png", ""   ; texture, sound
Data.b #True              ; opaque or not
Data.i 48                 ; solidity points lost when hit
Data.s "grass.png", ""
Data.b #True
Data.i 48
Data.s "wood.png", ""
Data.b #True
Data.i 48
Data.s "leaf.png", ""
Data.b #False
Data.i 128

Data.s "XXX END XXX"


;- Collision points datas
CollisionPointsForZMove:
Data.f -0.45, 0 ,-0.45
Data.f 0.45 , 0 ,-0.45
Data.f -0.45, 0 ,0.45
Data.f 0.45 , 0 ,0.45

Data.f -0.45, 0.9 ,-0.45
Data.f 0.45 , 0.9 ,-0.45
Data.f -0.45, 0.9 ,0.45
Data.f 0.45 , 0.9 ,0.45

Data.f -0.45, 1.8 ,-0.45
Data.f 0.45 , 1.8 ,-0.45
Data.f -0.45, 1.8 ,0.45
Data.f 0.45 , 1.8 ,0.45

Data.f -999999

CollisionPointsForXMove:
Data.f -0.45, 0 ,-0.45
Data.f -0.45 , 0 ,0.45
Data.f 0.45, 0 ,-0.45
Data.f 0.45 , 0 ,0.45

Data.f -0.45, 0.9 ,-0.45
Data.f -0.45 , 0.9 ,0.45
Data.f 0.45, 0.9 ,-0.45
Data.f 0.45 , 0.9 ,0.45

Data.f -0.45, 1.8 ,-0.45
Data.f -0.45 , 1.8 ,0.45
Data.f 0.45, 1.8 ,-0.45
Data.f 0.45 , 1.8 ,0.45

Data.f -999999

CollisionPointsForFalling:
Data.f -0.45, 0 ,-0.45
Data.f -0.45 , 0 ,0.45
Data.f 0.45, 0 ,-0.45
Data.f 0.45 , 0 ,0.45

Data.f -999999

CollisionPointsForJumping:
Data.f -0.45, 1.8 ,-0.45
Data.f -0.45 , 1.8 ,0.45
Data.f 0.45, 1.8 ,-0.45
Data.f 0.45 , 1.8 ,0.45

Data.f -999999
  
EndDataSection
N'oubliez pas cet include, désormais nécessaire:

Code : Tout sélectionner

;************************************************************************************
;-                       ---- Constants, structures, globals ----
;************************************************************************************
#PERLIN_B  = $100
#PERLIN_BM = $FF
#PERLIN_N  = $1000
#PERLIN_NP = 16   ;  2^N
#PERLIN_NM = $FF

Structure PERLIN_InnerDoubleArray
  d.d[0]
EndStructure

Global Dim  PERLIN_p.i(#PERLIN_B + #PERLIN_B + 1)
Global Dim PERLIN_g1.d(#PERLIN_B + #PERLIN_B + 1)
Global Dim PERLIN_g2.d(#PERLIN_B + #PERLIN_B + 1, 1)
Global Dim PERLIN_g3.d(#PERLIN_B + #PERLIN_B + 1, 2)
Global PERLIN_start.i = 1

;************************************************************************************
;-                                  ---- Macros ----
;************************************************************************************
Macro PERLIN_unsigned(value)
  ((value) + 1) / 2
EndMacro

Macro PERLIN_s_curve(t)
  ( t * t * ( 3 - 2 * t ) )
EndMacro

Macro PERLIN_lerp(t, a, b)
  ( a + t * (b - a) )
EndMacro

Macro PERLIN_setup(i,b0,b1,r0,r1)
  t  = vec(i) + #PERLIN_N
  b0 = Int(t) & #PERLIN_BM
  b1 = (b0 + 1) & #PERLIN_BM
  r0 = t - Int(t)
  r1 = r0 - 1.
EndMacro

Macro PERLIN_at2(rx,ry)
  ( rx * *q\d[0] + ry * *q\d[1] )
EndMacro

Macro PERLIN_at3(rx,ry,rz)
  ( rx * *q\d[0] + ry * *q\d[1] + rz * *q\d[2] )
EndMacro


;************************************************************************************
;-                                ---- Procedures ----
;************************************************************************************
Declare   PERLIN_initialize()
Declare.d PERLIN_noise1(arg.d)
Declare.d PERLIN_noise2(Array vec.d(1))
Declare.d PERLIN_noise3(Array vec.d(1))
Declare   PERLIN_normalize2(d.i)
Declare   PERLIN_normalize3(d.i)

Declare.d PERLIN_generateNoise1D(x.d, alpha.d, beta.d, n.i);
Declare.d PERLIN_generateNoise2D(x.d, y.d, alpha.d, beta.d, n.i);
Declare.d PERLIN_generateNoise3D(x.d, y.d, z.d, alpha.d, beta.d, n.i);


Procedure.d PERLIN_noise1(arg.d)
   Protected bx0.i, bx1.i
   Protected rx0.d, rx1.d, sx.d, t.d, u.d, v.d
   Protected Dim vec.d(1)

   vec(0) = arg
   If PERLIN_start <> 0
      PERLIN_start = 0
      PERLIN_initialize()
   EndIf

   PERLIN_setup(0,bx0,bx1,rx0,rx1)

   sx = PERLIN_s_curve(rx0)
   u = rx0 * PERLIN_g1( PERLIN_p( bx0 ) )
   v = rx1 * PERLIN_g1( PERLIN_p( bx1 ) )

   ProcedureReturn PERLIN_lerp(sx, u, v)
EndProcedure


Procedure.d PERLIN_noise2(Array vec.d(1))
  Protected bx0.i, bx1.i, by0.i, by1.i, b00.i, b10.i, b01.i, b11.i
  Protected rx0.d, rx1.d, ry0.d, ry1.d, *q.PERLIN_InnerDoubleArray, sx.d, sy.d, a.d, b.d, t.d, u.d, v.d
  Protected i.i, j.i
 
  If PERLIN_start <> 0
    PERLIN_start = 0
    PERLIN_initialize()
  EndIf
 
  PERLIN_setup(0, bx0,bx1, rx0,rx1)
  PERLIN_setup(1, by0,by1, ry0,ry1)
 
  i = PERLIN_p( bx0 )
  j = PERLIN_p( bx1 )
 
  b00 = PERLIN_p( i + by0 )
  b10 = PERLIN_p( j + by0 )
  b01 = PERLIN_p( i + by1 )
  b11 = PERLIN_p( j + by1 )
 
  sx = PERLIN_s_curve(rx0)
  sy = PERLIN_s_curve(ry0)
 
  *q = @PERLIN_g2( b00, 0 ) : u = PERLIN_at2(rx0,ry0)
  *q = @PERLIN_g2( b10, 0 ) : v = PERLIN_at2(rx1,ry0)
  a  = PERLIN_lerp(sx, u, v)
 
  *q = @PERLIN_g2( b01, 0 ) : u = PERLIN_at2(rx0,ry1)
  *q = @PERLIN_g2( b11, 0 ) : v = PERLIN_at2(rx1,ry1)
  b = PERLIN_lerp(sx, u, v)
 
  Protected rv.d = PERLIN_lerp(sy, a, b)
  ProcedureReturn rv
EndProcedure


Procedure.d PERLIN_noise3(Array vec.d(1))
  Protected bx0.i, bx1.i, by0.i, by1.i, bz0.i, bz1.i, b00.i, b10.i, b01.i, b11.i
  Protected rx0.d, rx1.d, ry0.d, ry1.d, rz0.d, rz1.d, *q.PERLIN_InnerDoubleArray, sy.d, sz.d, a.d, b.d, c.d, d.d, t.d, u.d, v.d
  Protected i.i, j.i

   If PERLIN_start <> 0
      PERLIN_start = 0
      PERLIN_initialize()
   EndIf

   PERLIN_setup(0, bx0,bx1, rx0,rx1);
   PERLIN_setup(1, by0,by1, ry0,ry1);
   PERLIN_setup(2, bz0,bz1, rz0,rz1);

   i = PERLIN_p( bx0 )
   j = PERLIN_p( bx1 )

   b00 = PERLIN_p( i + by0 )
   b10 = PERLIN_p( j + by0 )
   b01 = PERLIN_p( i + by1 )
   b11 = PERLIN_p( j + by1 )

   t  = PERLIN_s_curve(rx0)
   sy = PERLIN_s_curve(ry0)
   sz = PERLIN_s_curve(rz0)

   *q = @PERLIN_g3( b00 + bz0, 0 ) : u = PERLIN_at3(rx0,ry0,rz0)
   *q = @PERLIN_g3( b10 + bz0, 0 ) : v = PERLIN_at3(rx1,ry0,rz0)
   a = PERLIN_lerp(t, u, v)

   *q = @PERLIN_g3( b01 + bz0, 0 ) : u = PERLIN_at3(rx0,ry1,rz0);
   *q = @PERLIN_g3( b11 + bz0, 0 ) : v = PERLIN_at3(rx1,ry1,rz0);
   b = PERLIN_lerp(t, u, v);

   c = PERLIN_lerp(sy, a, b);

   *q = @PERLIN_g3( b00 + bz1, 0 ) : u = PERLIN_at3(rx0,ry0,rz1);
   *q = @PERLIN_g3( b10 + bz1, 0 ) : v = PERLIN_at3(rx1,ry0,rz1);
   a = PERLIN_lerp(t, u, v);

   *q = @PERLIN_g3( b01 + bz1, 0 ) : u = PERLIN_at3(rx0,ry1,rz1);
   *q = @PERLIN_g3( b11 + bz1, 0 ) : v = PERLIN_at3(rx1,ry1,rz1);
   b = PERLIN_lerp(t, u, v);

   d = PERLIN_lerp(sy, a, b);

   ProcedureReturn PERLIN_lerp(sz, c, d);
EndProcedure


Procedure PERLIN_normalize2(*v.PERLIN_InnerDoubleArray)
  Protected s.d = Sqr(*v\d[0] * *v\d[0] + *v\d[1] * *v\d[1])

  *v\d[0] = *v\d[0] / s
  *v\d[1] = *v\d[1] / s
EndProcedure


Procedure PERLIN_normalize3(*v.PERLIN_InnerDoubleArray)
  Protected s.d = Sqr(*v\d[0] * *v\d[0] + *v\d[1] * *v\d[1] + *v\d[2] * *v\d[2])

  *v\d[0] = *v\d[0] / s
  *v\d[1] = *v\d[1] / s
  *v\d[2] = *v\d[2] / s
EndProcedure


Procedure PERLIN_initialize()
  Protected i.i, j.i, k.i, tmp.i
  Protected *t.PERLIN_InnerDoubleArray
  
  i = 0
  While i < #PERLIN_B
    PERLIN_p(i)  = i
    tmp = ((Random(2147483647) % (#PERLIN_B + #PERLIN_B)) - #PERLIN_B)
    PERLIN_g1(i) = tmp / #PERLIN_B
   
 
    For j = 0 To 1
      tmp = ((Random(2147483647) % (#PERLIN_B + #PERLIN_B)) - #PERLIN_B)
      PERLIN_g2(i, j) = tmp / #PERLIN_B
    Next j
    PERLIN_normalize2(@PERLIN_g2(i, 0))
 
    For j = 0 To 2
      tmp = ((Random(2147483647) % (#PERLIN_B + #PERLIN_B)) - #PERLIN_B)
      PERLIN_g3(i, j) = tmp / #PERLIN_B
    Next j
    PERLIN_normalize3(@PERLIN_g3(i, 0))

    i + 1
  Wend 
 
  i - 1
  While i > 0
    i - 1
   
    k = PERLIN_p(i)
    j = Random(2147483647) % #PERLIN_B
    PERLIN_p(i) = PERLIN_p(j)
    PERLIN_p(j) = k;
  Wend
 
  i = 0
  While i < #PERLIN_B + 2
    PERLIN_p(#PERLIN_B + i) = PERLIN_p(i)
    PERLIN_g1(#PERLIN_B + i) = PERLIN_g1(i)

    For j = 0 To 1
      PERLIN_g2(#PERLIN_B + i, j) = PERLIN_g2(i, j)
    Next j
    For j = 0 To 2
      PERLIN_g3(#PERLIN_B + i, j) = PERLIN_g3(i, j)
    Next j

    i + 1
  Wend
EndProcedure


Procedure.d PERLIN_generateNoise1D(x.d, alpha.d, beta.d, iterations.i)
   Protected i.i
   Protected val.d = 0, sum.d = 0
   Protected p.d = 1, scale.d = 1

   p = x
   For i = 1 To iterations
      val = PERLIN_noise1(p)
      sum + val / scale
      scale * alpha
      p * beta
   Next i
   
   ProcedureReturn(sum)
EndProcedure


Procedure.d PERLIN_generateNoise2D(x.d ,y.d, alpha.d, beta.d, iterations.i)
   Protected i.i
   Protected val.d = 0, sum.d = 0
   Protected scale.d = 1
   Protected Dim args.d(1)

   args(0) = x
   args(1) = y
   For i = 1 To iterations
      val = PERLIN_noise2(args())
      sum + val / scale
      scale * alpha
      args(0) * beta
      args(1) * beta
   Next i
   
   ProcedureReturn(sum)
EndProcedure


Procedure.d PERLIN_generateNoise3D(x.d, y.d, z.d, alpha.d, beta.d, iterations.i)
   Protected i.i
   Protected val.d = 0, sum.d = 0
   Protected scale.d = 1
   Protected Dim args.d(2)

   args(0) = x
   args(1) = y
   args(2) = z
   For i = 1 To iterations
      val = PERLIN_noise3(args())
      sum = sum + (val / scale)
      scale * alpha
      args(0) * beta
      args(1) * beta
      args(2) * beta
   Next i
   
   ProcedureReturn(sum)
EndProcedure

Dernière modification par kelebrindae le ven. 22/nov./2013 18:07, modifié 6 fois.
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Créer un monde "à la minecraft"

Message par falsam »

Bonjour kelebrindae. Whaou mais c'est magnifique. j'ai commencé à creuser un trou pour me cacher. C'est comme l'original. Enfin presque :). A quand l'inventaire et les zombies. J’espère que tu envisages une version jouable hein ?

Quand au problème de l'effondrement du FPS je passe de 310 dans le contexte d'une vue normale, à 55 Fps en vue éloignée. On est rarement dans cette vue là quand on joue.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Créer un monde "à la minecraft"

Message par G-Rom »

peu être en créant à chaque changement d'état de la géométrie statique , j'ai afficher plus d'un million de polygone à 60fps sur une bécane médium quand j'ai wrappé cette partie pour PB. par contre je ne sais pas se que donnerais la création de géométrie statique à la volée.
j'ai pas regardé ton code en détails , mais élimine tu les faces inutiles ?
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Créer un monde "à la minecraft"

Message par kelebrindae »

@Falsam:
Une telle baisse de perf pour 20000 ou 25000 triangles, c'est quand même un peu louche. Et puis dans Minecraft, le monde s'étend à perte de vue; ici, on n'en a qu'un tout petit bout...

@G-rom:
En résumé, j'ai un gros tableau de 32x128x32. Chaque case de ce tableau (= un cube) peut être remplie ou non, et selon que les cases autour sont remplies, les faces du cube sont visibles ou non.
- Pour un cube tout seul en l'air, toutes les faces sont visibles;
- Pour un cube entièrement "enterré" sous les autres, aucune face n'est visible;
- Pour un cube qui affleure au niveau du sol, seule la face du dessus est visible.

Si aucune face n'est visible, je ne crée rien. Sinon, je crée à l'emplacement du cube une entity en utilisant un des 63 meshes à ma disposition (un par combinaison de faces visibles/cachées):
- le mesh n°1 (%000001) a seulement 2 triangle: la face du dessus;
- le mesh n°2 (%000010) a seulement 2 triangle: la face du dessous;
- le mesh n°3 (%000011) a quatre triangles: le dessus et le dessous;
etc...
Et quand on détruit un cube, scanne les 26 qui sont autour afin d'effacer/créer les entities mises à jour.
Donc, et je crois que le programme fonctionne bien de ce côté, il n'y a pas un triangle de trop dans le rendu.

Maintenant, le faire en géométrie statique, je ne sais pas si c'est possible. Cela veut dire que quand on détruit un cube, il faut tout effacer et recréer tout le groupe statique (pas juste les cubes voisins du cube détruit); Mais pourquoi pas, ce n'est encore pas trop long.
Par contre, il faudrait se passer des ombres, car il me semble que la géométrie statique ne pouvait pas projeter d'ombre... Et puis il faut voir si Ogre supporte bien qu'on mette constamment à jour de la géométrie censée être statique...
Mais bon, ça vaut le coup d'essayer! :)
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Créer un monde "à la minecraft"

Message par falsam »

Le monde s'étend à perte de vue mais est ce que tu joues avec une vue pareille ? Si tu réduis la distance visible je pense que tu n'auras plus de souci de FPS.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Créer un monde "à la minecraft"

Message par G-Rom »

Sans passé par les meshs statique, il faudrais que tu découpes ton monde en portion de 65535 vertices ( je te laisse faire le calcul de taille )
car ton problème , c'est le nombre d'entité, et non pas le nombre de triangles, il faudrais que tu agises au niveau du mesh, et non plus en créant des meshs à la volée , ce qui parait naturel mais absolument pas optimisé, la carte graphique aime bien dessiner en une fois, chaque appel à la fonction de rendu en interne à un cout élevé en terme de performance, plus il y a d'entité visible, plus ça rame. Regarde du coté des octree.
et au lieu d'avoir 10000 entité , tu en aura qu'une poignée voir même une seule en fonction du découpage de ton monde, cela s'appelle le partitionnement de l'espace.
ps: les static geometry supportent les shadow.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Créer un monde "à la minecraft"

Message par kelebrindae »

:D ça valait le coup d'essayer ! :D

Avec un code "brut de fonderie" qui utilise un groupe statique, plus de problème de perfs, mes 14000 et quelques polys sont affichés à 175 FPS. Et l'ombrage est géré par le groupe statique (une nouveauté de la 5.20, non?).

Seul inconvénient: adieu le mouse-picking sur les entities et la possibilité d'utiliser la physique "Bullet". Mais bon, on doit pouvoir faire ça avec des calculs...

Autre chose: la recréation complète du monde est relativement rapide, et quasiment imperceptible quand le Debugger est désactivé; en optimisant un peu (genre, sans recréer toutes les entités à chaque fois), ça doit être jouable.

Bref: Merci G-rom!

Code modifié, pas optimisé au niveau de la re-création du monde quand un cube est détruit, et sans mouse-picking:

Code : Tout sélectionner

;************************************************************************************
;  Minecraft clone
;  PB version: 5.20
;  Date: November, 01, 2013
;
;- In game:
;- L  -> Day / night
;- P  -> Display physic bodies
;- W -> Wireframe view
;
;************************************************************************************


;************************************************************************************
;-                          ---- Constants and structures ----
;************************************************************************************

; Window size
#SCREENWIDTH = 1000
#SCREENHEIGHT = 500

; Camera
#CAMERASPEED = 0.2
#CAMERA = 0

; Sides of the cube
Enumeration
  #TOPSIDE
  #BOTTOMSIDE
  #LEFTSIDE
  #RIGHTSIDE
  #FRONTSIDE
  #BACKSIDE
EndEnumeration


#CHUNKLENGTH = 31
#CHUNKHEIGHT = 63
#CHUNKWIDTH = 31
#CHUNKNUMOBJ = 1  ; max number of object at one x,y,z location in the matrix

Global.i CHUNKWIDTHBITS
If CHUNKWIDTHBITS > 1
  CHUNKWIDTHBITS = Int(Log(#CHUNKNUMOBJ) / Log(2)) + 1
Else
  CHUNKWIDTHBITS = 0
EndIf
Global.i CHUNKHEIGHTBITS = CHUNKWIDTHBITS + Int(Log(#CHUNKWIDTH) / Log(2)) + 1
Global.i CHUNKLENGTHBITS = CHUNKHEIGHTBITS + Int(Log(#CHUNKHEIGHT) / Log(2)) + 1

; Cube materials (ground, grass, wood, stone, etc..
Structure cubeMat_struct
  mtIntact.i
  mtDamaged.i
  mtAlmostBroken.i
  
  opaque.b
  soundWhenHit.i  ; sound played when left-clicked
  ptLossWhenHit.i ; solidity points lost when left-clicked
EndStructure
Global Dim cubeMat.cubeMat_struct(128) ; 127 cube types maximum (.b dans cube_struct, 0 = empty)

; World infos
Structure cube_struct
  numMat.b
  solidity.b ; 127 = intact, 0 = destroyed
EndStructure
Global Dim chunk.cube_struct(#CHUNKLENGTH+1,#CHUNKHEIGHT+1,#CHUNKWIDTH+1) ; x,y,z


; In a cube, there's six sides, and each one can be either visible or not.
; => 64 possible configurations (%111111)
Global Dim cubeMesh.i(%111111 + 1)
Global Dim faceValue.i(6)  ; Top = %000001, Bottom = %000010, Left = %000100, etc..

; Misc
Global x.i,y.i,z.i
Global wmain.i, wireframe.b, viewBodies.b, dontQuit.b = #True, eventId.i, mouseX.f, mouseY.f
Global temp.i, isDayTime.b
Global fpsSprite.i, fpsTimer.i, showFps.b = #True, nbCubeEntities.i

EnableExplicit

;************************************************************************************
;-                                 ---- Macros ----
;************************************************************************************
Macro DISPLAY_FPS(timer)
    ; Display FPS
    If showFps = #True
      If timer - fpsTimer > 1000
        StartDrawing(SpriteOutput(fpsSprite))
        DrawText(0,0,Str(nbCubeEntities) + " entities, " + Str(CountRenderedTriangles()) + " rendered polys, " +  Str(Engine3DFrameRate(#PB_Engine3D_Current))+ " FPS   " )
        StopDrawing()
        fpsTimer = timer
      EndIf
      DisplayTransparentSprite(fpsSprite,0,0) 
    EndIf    
EndMacro

;************************************************************************************
;-                                 ---- Procedures ----
;************************************************************************************

; Read and initialize the material
Procedure initializeMaterials()
  Protected numMat.i
  Protected matName.s, soundName.s
  
  Restore MaterialDatas
  Read.s matName
  While matName <> "XXX END XXX"
    numMat+1
    ; Texture
    cubeMat(numMat)\mtIntact = CreateMaterial(#PB_Any,TextureID(LoadTexture(#PB_Any,matName)) )
    MaterialFilteringMode(cubeMat(numMat)\mtIntact, #PB_Material_Anisotropic, 6)
    
    ; Sound when hit
    Read.s soundName
    If soundName <> ""
      cubeMat(numMat)\soundWhenHit = LoadSound(#PB_Any,soundName)
    EndIf
    
    ; Opaque or not
    Read.b cubeMat(numMat)\opaque
    If cubeMat(numMat)\opaque = #False
      MaterialBlendingMode(cubeMat(numMat)\mtIntact, #PB_Material_AlphaBlend)
    EndIf
    
    ; "Damaged" materials
    cubeMat(numMat)\mtDamaged = CopyMaterial(cubeMat(numMat)\mtIntact,#PB_Any)
    ScrollMaterial(cubeMat(numMat)\mtDamaged,0,0.25,#PB_Material_Fixed)
    cubeMat(numMat)\mtAlmostBroken = CopyMaterial(cubeMat(numMat)\mtIntact,#PB_Any)
    ScrollMaterial(cubeMat(numMat)\mtAlmostBroken,0,0.50,#PB_Material_Fixed)  
    
    ; Solidity points lost when hit
    Read.i cubeMat(numMat)\ptLossWhenHit
    
    Read.s matName
  Wend  
  
EndProcedure

; Load or Build the needed meshes
Procedure buildCubeMeshes()
  Protected i.i,j.i, k.i
  Protected nbVert.i,nbFaces.i
  Protected x.f,y.f,z.f
  Protected nx.f,ny.f,nz.f
  Protected u.f,v.f
  Protected v1.i,v2.i,v3.i
  Protected vertStart.i
  
  ; Create the directory where meshes are stored
  If FileSize("meshes") <> -2
    CreateDirectory("meshes")
  EndIf
  
  ; Associate each face with a bit position in a 6 digits binary number.
  ; Top = %000001, Bottom = %000010, Left = %000100, etc..
  For i=#TOPSIDE To #BACKSIDE
    faceValue(i) = Pow(2,i)
  Next i
  
  ; For each combination, create the mesh.
  For i=%000001 To %111111
    
    ; Load the mesh
    cubeMesh(i) = LoadMesh(#PB_Any,"cube_" + RSet(Bin(i),6,"0") + ".mesh")
    If cubeMesh(i) <> 0
      Continue
    EndIf
    
    ; Create the mesh
    Debug "Mesh " + "cube_" + RSet(Bin(i),6,"0") + ".mesh" + " not found; building it..."
    vertStart = 0
    cubeMesh(i) = CreateMesh(#PB_Any)
    
    For j=#TOPSIDE To #BACKSIDE
      ; If the mesh must not have the current side, skip to the next.
      If i & faceValue(j) = 0
        Continue
      EndIf
      
      ; Get the correct datas
      Select j
        Case #TOPSIDE
          Restore meshTopDatas
        Case #BOTTOMSIDE
          Restore meshBottomDatas
        Case #LEFTSIDE
          Restore meshLeftDatas
        Case #RIGHTSIDE
          Restore meshRightDatas
        Case #FRONTSIDE
          Restore meshFrontDatas
        Case #BACKSIDE
          Restore meshBackDatas
      EndSelect
      
      ; Read number of vertices and triangles
      Read.i nbVert:Read.i nbFaces
      
      ; Add the vertices to mesh
      For k = 1 To nbVert  
        Read.f x:Read.f y:Read.f z
        Read.f nx:Read.f ny:Read.f nz
        Read.f u:Read.f v
        
        MeshVertexPosition(x,y,z)
        MeshVertexNormal(nx,ny,nz)
        MeshVertexTextureCoordinate(u,v)
      Next k
     
      ; Add the triangles to mesh
      For k = 1 To nbFaces
        Read.i v1:Read.i v2:Read.i v3
        MeshFace(v1+vertStart,v2+vertStart,v3+vertStart)
      Next k
      
      
      vertstart + nbVert
    Next j
  
    ; Finalize mesh
    FinishMesh(#True)
    BuildMeshShadowVolume(cubeMesh(i))
    BuildMeshTangents(cubeMesh(i))      

    SaveMesh(cubeMesh(i),"meshes/cube_" + RSet(Bin(i),6,"0") + ".mesh")
  Next i
 

EndProcedure


; Initialize a random world
Procedure buildWorld()
  Protected i.i,x.i,y.i,z.i
  
  For y = 0 To 20
    For x = 0 To #CHUNKLENGTH
      For z = 0 To #CHUNKWIDTH
        chunk(x,y,z)\numMat = 1
      Next z
    Next x
  Next y
  
  i=5
  For y = 21 To 25
    For x = 0 To i
      For z = 0 To i
        chunk(6+x,y,6+z)\numMat = 1
        chunk(6-x,y,6-z)\numMat = 1
        chunk(6+x,y,6-z)\numMat = 1
      Next z
    Next x
    i-1
  Next y
  
  i=4
  For y = 0 To i
    For x = 0 To i
      For z = 0 To i
        chunk(9+x,40+y,9+z)\numMat = 1
        chunk(9-x,40+y,9-z)\numMat = 1
        chunk(9+x,40-y,9+z)\numMat = 1
        chunk(9-x,40-y,9-z)\numMat = 1
      Next z
    Next x
    i-1
  Next y
  
    
  ; Tree
  chunk(19,21,19)\numMat = 3
  chunk(19,22,19)\numMat = 3
  chunk(19,23,19)\numMat = 3
  chunk(19,24,19)\numMat = 3
  i=2
  For y = 0 To i
    For x = 0 To i
      For z = 0 To i
        If chunk(18+x,23+y,18+z)\numMat = 0 And Random(100) > 25
          chunk(18+x,23+y,18+z)\numMat = 4
        EndIf
      Next z
    Next x
  Next y

  ; Post-traitement: les cubes de terre avec rien au-dessus sont en herbe
  For y = 0 To #CHUNKHEIGHT
    For x = 0 To #CHUNKLENGTH
      For z = 0 To #CHUNKWIDTH
        If chunk(x,y,z)\numMat > 0
          chunk(x,y,z)\solidity = 127
          If chunk(x,y,z)\numMat = 1 And y < #CHUNKHEIGHT And chunk(x,y+1,z)\numMat = 0
            chunk(x,y,z)\numMat = 2
          EndIf
        EndIf
      Next z
    Next x
  Next y
    
  
EndProcedure


; Create the entities for the visibles cubes
Procedure displayWorld(xOri.i=0,yOri.i=0,zOri.i=0,xEnd.i=#CHUNKLENGTH,yEnd.i=#CHUNKHEIGHT,zEnd.i=#CHUNKWIDTH)
  Protected x.i,y.i,z.i,i.i
  Protected numEntity.i, visibleSides.i, mat.i
  
  CreateStaticGeometry(0, #CHUNKWIDTH, #CHUNKHEIGHT, #CHUNKLENGTH, #True)

  For y = yOri To yEnd
    For x = xOri To xEnd
      For z = zOri To zEnd
        ; The entity's number is a function of its position.
        numEntity = x<<CHUNKLENGTHBITS | y<<CHUNKHEIGHTBITS | z << CHUNKWIDTHBITS 
        
        ; If there's already an entity here, delete it.
        If IsEntity(numEntity)
          Debug "Deleting Entity " + Str(numEntity) + " : Coords = " + Str(EntityX(numEntity)) + "," + Str(EntityY(numEntity)) + "," + Str(EntityZ(numEntity))          
          FreeEntity(numEntity)
          nbCubeEntities-1
        EndIf
        
        ; Check if there's anything at x,y,z
        If chunk(x,y,z)\numMat > 0
          ; Which sides of the cube are visible ?
          visibleSides = 0
          
          ; Top          
          If y < #CHUNKHEIGHT And cubeMat(chunk(x,y+1,z)\numMat)\opaque = 0
            visibleSides | faceValue(#TOPSIDE)
          EndIf
  
          ; Bottom
          If y > 0 And cubeMat(chunk(x,y-1,z)\numMat)\opaque  = 0
            visibleSides | faceValue(#BOTTOMSIDE)
          EndIf
  
          ; Left
          If x > 0 And cubeMat(chunk(x-1,y,z)\numMat)\opaque = 0
            visibleSides | faceValue(#LEFTSIDE)
          EndIf
  
          ; Right
          If x < #CHUNKLENGTH And cubeMat(chunk(x+1,y,z)\numMat)\opaque = 0
            visibleSides | faceValue(#RIGHTSIDE)
          EndIf
          
          ; Front
          If z < #CHUNKWIDTH And cubeMat(chunk(x,y,z+1)\numMat)\opaque = 0
            visibleSides | faceValue(#FRONTSIDE)
          EndIf
  
          ; Back
          If z > 0 And cubeMat(chunk(x,y,z-1)\numMat)\opaque = 0
            visibleSides | faceValue(#BACKSIDE)
          EndIf
          
          ; If there's at least one visible face, create the entity with the correct material
          If visibleSides > 0
            If chunk(x,y,z)\solidity <= 64
              mat = MaterialID(cubeMat(chunk(x,y,z)\numMat)\mtAlmostBroken)
            ElseIf chunk(x,y,z)\solidity <= 96
              mat = MaterialID(cubeMat(chunk(x,y,z)\numMat)\mtDamaged)
            Else
              mat = MaterialID(cubeMat(chunk(x,y,z)\numMat)\mtIntact)
            EndIf
            
            DisableDebugger ; To avoid message: "Entity number is very high; is it normal?"
            CreateEntity(numEntity,MeshID(cubeMesh(visibleSides)),mat,x,y,z,$00001)
            ;EntityPhysicBody(numEntity,#PB_Entity_StaticBody,0,0.8,1) ; costs 5-10 FPS with 1000 entities
            nbCubeEntities+1
            EnableDebugger
            AddStaticGeometryEntity(0, EntityID(numEntity), x, y, z)
            FreeEntity(numEntity)
          EndIf

        EndIf

      Next z
    Next x
  Next y
  
  BuildStaticGeometry(0)
EndProcedure


Procedure deleteCube(x.i,y.i,z.i)
  Protected i.i
  Protected xOri.i,yOri.i,zOri.i
  Protected xEnd.i,yEnd.i,zEnd.i
  
  ; Delete the block
  If chunk(x,y,z)\solidity =< 0
    chunk(x,y,z)\numMat = 0
    
    ; Set the limits of the borders scan
    xOri = x-1
    If xOri < 0: xOri = 0 : EndIf
    yOri = y-1
    If yOri < 0: yOri = 0 : EndIf
    zOri = z-1
    If zOri < 0: zOri = 0 : EndIf
    xEnd = x+1
    If xEnd > #CHUNKLENGTH: xEnd = #CHUNKLENGTH : EndIf
    yEnd = y+1
    If yEnd > #CHUNKHEIGHT: yEnd = #CHUNKHEIGHT : EndIf
    zEnd = z+1
    If zEnd > #CHUNKWIDTH: zEnd = #CHUNKWIDTH : EndIf
  Else
    ; The block is still here; no need to redraw the surroundings.
    xOri = x: yOri=y: zOri = z
    xEnd = x: yEnd=y: zEnd = z
  EndIf
  
  ;ClearDebugOutput()
  ;Debug Str(xOri) + "," + Str(yOri) + "," + Str(zOri) + " => " + Str(xEnd) + "," + Str(yEnd) + "," + Str(zEnd)
  
  ; Update the display with the changes we've just made.
  ;displayWorld(xOri,yOri,zOri,xEnd,yEnd,zEnd)  ; redraw only what's necessary 
  displayWorld()  ; redraw all
  
EndProcedure

DisableExplicit

;************************************************************************************
;-                                 ---- Main program ----
;************************************************************************************

;- Initialization
InitEngine3D()
InitSprite()
InitKeyboard()
InitMouse()
UsePNGImageDecoder()

Add3DArchive("textures",#PB_3DArchive_FileSystem)
Add3DArchive("meshes",#PB_3DArchive_FileSystem)

;- Window
OpenWindow(Wmain,0, 0, #SCREENWIDTH,#SCREENHEIGHT ,"Cube environnement",#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(Wmain), 0, 0, #SCREENWIDTH,#SCREENHEIGHT, 0, 0, 0,#PB_Screen_NoSynchronization)

;- Camera
CreateCamera(#CAMERA, 0, 0, 100, 100)
MoveCamera(#CAMERA,0,25,8,#PB_Absolute)
CameraBackColor(#CAMERA,$000000)
CameraLookAt(#CAMERA,16,20,16)

;-Light
CreateLight(1,$FFFFFF,CameraX(#CAMERA),CameraY(#CAMERA),CameraZ(#CAMERA))
LightAttenuation(1,20,0)
WorldShadows(#PB_Shadow_Additive)
Fog($000000,50,10,30)


;- Meshes
buildCubeMeshes()

; 3d cursor
cursorMesh = CreateCube(#PB_Any,1.1)
mtCursor = CreateMaterial(#PB_Any,TextureID(LoadTexture(#PB_Any,"cursor.png")))
DisableMaterialLighting(mtCursor,#True)
MaterialShadingMode(mtCursor,#PB_Material_Flat|#PB_Material_Wireframe)
ScrollMaterial(mtCursor, 1, 1, #PB_Material_Animated)
cursor = CreateEntity(#PB_Any,MeshID(cursorMesh),MaterialID(mtCursor),16,22,16,$00010)
EntityRenderMode(cursor,0)
DisableEntityBody(cursor,#True)


;-Materials
initializeMaterials()

;- World
buildWorld()
displayWorld()


;- Sprite used to display FPS
fpsSprite = CreateSprite(#PB_Any,640,20)


;- Main loop
KeyboardMode(#PB_Keyboard_International)
Repeat
  Delay(1)
  
  ; Windows events
  eventID = WindowEvent()
  While eventID <> 0 And dontQuit = #True
    eventID = WindowEvent()
  Wend
  ExamineKeyboard()
  
  
  ;- Function keys (W, P, L)
  ; Activate wireframe render
  If KeyboardReleased(#PB_Key_W)
    wireframe = 1-wireframe
    If wireframe = #True
      CameraRenderMode(0,#PB_Camera_Wireframe)
    Else
      CameraRenderMode(0,#PB_Camera_Textured)
    EndIf
  EndIf
  
  ; Activate physics render
  If KeyboardReleased(#PB_Key_P)
    viewBodies = 1-viewBodies
    If viewBodies  = #True
      WorldDebug(#PB_World_DebugBody)
    Else
      WorldDebug(#PB_World_DebugNone)
    EndIf
  EndIf
  
  ; Change day / night
  If KeyboardReleased(#PB_Key_L)
    isDayTime = 1 - isDayTime
    If isDayTime = #True
      AmbientColor($777777)
      CreateLight(0,$FFFFFF,2000,4000,3000)
      CameraBackColor(#CAMERA,$D9A789)
      LightAttenuation(1,10,1)
      Fog($C2AF80,20,10,100)
    Else
      FreeLight(0)
      CameraBackColor(#CAMERA,$000000)
      LightAttenuation(1,20,0)
      Fog($000000,50,10,30)
    EndIf
  EndIf
  
  ;- Move camera (mouse + arrows)
  If ExamineMouse()
    MouseX = -(MouseDeltaX()/20)
    MouseY = -(MouseDeltaY()/20)
  EndIf
 
  RotateCamera(#CAMERA, MouseY, MouseX, 0, #PB_Relative)
  If KeyboardPushed(#PB_Key_Up)
    MoveCamera  (#CAMERA, 0, 0, -#CAMERASpeed)
  EndIf
  If KeyboardPushed(#PB_Key_Down)
    MoveCamera  (#CAMERA, 0, 0, #CAMERASpeed)
  EndIf

  If KeyboardPushed(#PB_Key_Left)
    MoveCamera  (#CAMERA, -#CAMERASpeed, 0, 0)
  EndIf
  If KeyboardPushed(#PB_Key_Right)
    MoveCamera  (#CAMERA, #CAMERASpeed, 0, 0)
  EndIf
  MoveLight(1,CameraX(#CAMERA),CameraY(#CAMERA),CameraZ(#CAMERA),#PB_Absolute)
  
  
  ;- Left button click
  objPicked = MousePick(#CAMERA, #SCREENWIDTH / 2, #SCREENHEIGHT / 2,$00001)
  If objPicked >= -1
    HideEntity(cursor,#False)
    ;xPick = Int(EntityX(objPicked))
    ;yPick = Int(EntityY(objPicked))
    ;zPick = Int(EntityZ(objPicked))
    xPick = 16:yPick = 20: zPick = 16
    MoveEntity(cursor,xPick,yPick,zPick, #PB_Absolute)
    
    If MouseButton(#PB_MouseButton_Left) <> 0
      If mouseClickable = #True
        mouseClickable = #False
        
        If cubeMat(chunk(xPick,yPick,zPick)\numMat)\soundWhenHit <> 0
          PlaySound(cubeMat(chunk(xPick,yPick,zPick)\numMat)\soundWhenHit,#PB_Sound_MultiChannel)
        EndIf
        
        chunk(xPick,yPick,zPick)\solidity - cubeMat(chunk(xPick,yPick,zPick)\numMat)\ptLossWhenHit
        deleteCube(xPick,yPick,zPick)
      EndIf
    Else
      mouseClickable = #True
    EndIf

  Else
    HideEntity(cursor,#True)
  EndIf
     
  ;- Render
  RenderWorld()
  
  ;- Display FPS
  DISPLAY_FPS(ElapsedMilliseconds())
  FlipBuffers()

Until KeyboardPushed(#PB_Key_Escape) Or dontQuit = #False

End


;- Meshes datas
DataSection
  meshTopDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.5,0.5,-0.5
  Data.f 0,1,0
  Data.f 0,0
  Data.f 0.5,0.5,-0.5
  Data.f 0,1,0
  Data.f 0.25,0
  Data.f 0.5,0.5,0.5
  Data.f 0,1,0
  Data.f 0.25,0.25
  Data.f -0.5,0.5,0.5
  Data.f 0,1,0
  Data.f 0,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2
  
MeshBottomDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.50,-0.50,0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.50,0.00
  Data.f 0.50,-0.50,0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.75,0.00
  Data.f 0.50,-0.50,-0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.75,0.25
  Data.f -0.50,-0.50,-0.50
  Data.f 0.00,-1.00,0.00
  Data.f 0.50,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshLeftDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.50,0.50,-0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.25,0.00
  Data.f -0.50,0.50,0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.50,0.00
  Data.f -0.50,-0.50,0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.50,0.25
  Data.f -0.50,-0.50,-0.50
  Data.f -1.00,0.00,0.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshRightDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f 0.50,0.50,0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.25,0.00
  Data.f 0.50,0.50,-0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.50,0.00
  Data.f 0.50,-0.50,-0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.50,0.25
  Data.f 0.50,-0.50,0.50
  Data.f 1.00,0.00,0.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshFrontDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f -0.50,0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.25,0.00
  Data.f 0.50,0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.50,0.00
  Data.f 0.50,-0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.50,0.25
  Data.f -0.50,-0.50,0.50
  Data.f 0.00,0.00,1.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2

MeshBackDatas:
  ; Nb vertices, nb triangles
  Data.i 4,2
  ; Vertices: pos / normals / uv
  Data.f 0.50,0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.25,0.00
  Data.f -0.50,0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.50,0.00
  Data.f -0.50,-0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.50,0.25
  Data.f 0.50,-0.50,-0.50
  Data.f 0.00,0.00,-1.00
  Data.f 0.25,0.25
  ; Faces
  Data.i 2,1,0
  Data.i 0,3,2
  
;- Materials datas
MaterialDatas:
Data.s "ground.png", ""   ; texture, sound
Data.b #True              ; opaque or not
Data.i 48                 ; solidity points lost when hit
Data.s "grass.png", ""
Data.b #True
Data.i 48
Data.s "wood.png", ""
Data.b #True
Data.i 48
Data.s "leaf.png", ""
Data.b #False
Data.i 128

Data.s "XXX END XXX"
  
  
EndDataSection
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Créer un monde "à la minecraft"

Message par falsam »

Avec ce nouveau code tu ne peux plus détruire de face. A quoi sert le Delay() dans ta boucle ?
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Créer un monde "à la minecraft"

Message par G-Rom »

Les ombres ont toujours été possible, la physique aussi d'ailleurs (statique uniquement) ainsi que le picking normalement.
Bizarre ci cela ne marche pas.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Créer un monde "à la minecraft"

Message par kelebrindae »

@Falsam:
Oui, plus de destruction car plus de mouse-picking. Le curseur est quand même fixé au milieu de la carte pour pouvoir détruire au moins un cube. Quand au délai, il sert à relâcher un peu le proc à chaque boucle (vieille habitude sujette à polémique...).

Pour le mouse-picking, il faut que je fasse une intersection entre un rayon projeté depuis le milieu de la caméra et la matrice stockant le monde pour trouver la plus proche case remplie sur cet axe (je ne suis pas sûr d'être très clair, là).
Quelqu'un sait comment faire?

@G-Rom:
Ah bon? Pour les ombres, il me semble que j'avais testé dans une précédente version et qu'on n'avait pas le paramètre; je dois me tromper.
Quant à la physique, j'avoue que je n'ai même pas essayé. Comment fait-on pour associer un body physique au groupe statique, vu que l'on efface les entities après le build?
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Créer un monde "à la minecraft"

Message par G-Rom »

quand tu rajoute une entité de mémoire, une copie de son body est fait en interne.
Pour le picking passe par raypick()
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Créer un monde "à la minecraft"

Message par kelebrindae »

Oui, mais comme j'efface l'entité après l'avoir ajoutée au groupe, le body aussi est effacé. Ceci dit, je pourrais me contenter de la cacher, ce qui me ferait gagner du temps quand je dois recréer le monde. A creuser...
Et pour le ray-pick, je vais regarder.

Encore merci! :D
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

Re: Créer un monde "à la minecraft"

Message par kelebrindae »

Non: raypick ne détecte pas les statiques.
Je vais me débrouiller autrement...
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Créer un monde "à la minecraft"

Message par G-Rom »

Logiquement si, avec raycollide de mémoire tu peu récupéré l'endroit précis d'une collision.
Avatar de l’utilisateur
venom
Messages : 3128
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: Créer un monde "à la minecraft"

Message par venom »

Excellent, très ressemblant bravo :wink:






@++
Windows 10 x64, PureBasic 5.73 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
Répondre