Créer un monde "à la minecraft"

Généralités sur la programmation 3D
G-Rom
Messages : 3627
Inscription : dim. 10/janv./2010 5:29

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

Message par G-Rom »

Oui , c'est ca, chaque type de bloc, un seul mesh.
Pour le thread, ca ne résoudra rien, je ne vois pas l’intérêt de parallélisé , cela complexifiera ton code plus qu'autre chose.
Voit ton monde comme un cube de 0 & de 1, chaque cube à une taille ( taille de chunck ) ton monde est composé de multiple cube ( l'idéal est de les créer au préalable dans un fichier que tu lis en stream à fur & à mesure que tu avances, tu épargne ainsi la RAM). quand tu changes ton cube ( rajout ou suppression de bloc ), tu met à jour le chunck correspondant.
Pour les tableaux, il faut passer des pointeurs , par exemple :
Dim vertexInfos.PB_vertex(3,0)
GetMeshData(cubeMesh(i),0,vertexInfos(2, ), [etc..]
je pense qu'il te manque '@' avant le tableau , car là tu lui passe la valeur de l'indice , et non pas le pointeur.
C'est pas moi qui est implémenté les mesh manuel , essaye de contacté Comtois, je crois qu'il à bossé dessus.
Essaye d'implémenté la triangularisation de delaunay, ca te simplifiera la vie pour faire des mesh à la volée.
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

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

Message par graph100 »

kelebrindae a écrit : - Est-il possible de faire des "GetMeshDatas" ou des "CopyArray" avec un tableau de tableaux ou un tableau situé dans une structure? Parce que je voulais stocker les infos des meshes élémentaires au lieu de faire des "GetMeshDatas" à chaque fois, et je n'ai pas réussi.
Exemple:

Code : Tout sélectionner

Dim vertexInfos.PB_vertex(3,0)
GetMeshData(cubeMesh(i),0,vertexInfos(2, ), [etc..]
ou bien

Code : Tout sélectionner

Stucture vertexInfos_struct
  infos.PB_vertex()
endstructure
Dim vertexInfos.vertexInfos_struct(3)
GetMeshData(cubeMesh(i),0,vertexInfos\infos(), [etc..]
Je ne suis pas parvenu à trouver une solution pour ça. Idem pour "CopyArray". :?
Ce n'est pas possible?
Je ne peux pas te répondre sur la fonction 3D, mais si tu déclares ton tableau correctement ça fonctionnera mieux :

Code : Tout sélectionner

Structure vertexInfos_struct
	Array infos.PB_vertex(0)
EndStructure


Define vertexInfos.vertexInfos_struct

Dim vertexInfos\infos(3)

GetMeshData(cubeMesh(i), 0, vertexInfos\infos(), [etc..])
ensuite un exemple de code fonctionnel pour la copie de tableau depuis une structure (je l'utilise dans tout mes prog depuis que ça a été ajouté dans PB :mrgreen: )

Code : Tout sélectionner

Structure exemple
	Array infos.POINT(0)
EndStructure


Define a.exemple

Dim a\infos(3)

For i = 0 To ArraySize(a\infos()) ; toutes les commandes de tableaux fonctionnent sur les tableaux des structures !)
	a\infos(i)\x = i * 5
	a\infos(i)\y = i * 10
Next

Define b.exemple

CopyArray(a\infos(), b\infos())
FreeArray(a\infos())

For i = 0 To ArraySize(b\infos())
	Debug "b\infos(i) X:Y = " + b\infos(i)\x + " : " + b\infos(i)\y
Next
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

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

Message par falsam »

kelebrindae a écrit : - Est-il possible de faire des "GetMeshDatas" ou des "CopyArray" avec un tableau de tableaux ou un tableau situé dans une structure? Parce que je voulais stocker les infos des meshes élémentaires au lieu de faire des "GetMeshDatas" à chaque fois, et je n'ai pas réussi.
Oui on peut faire un CopyArray d'un tableau vers un tableau ou bien un GetMeshDatas dans un élément d'une liste incluant un tableau.

Obtenir les données internes du mesh passe par une structure prédéfinie de type PB_MeshVertex.
Juste pour les curieux

Code : Tout sélectionner

Structure PB_MeshVertex
    x.f
    y.f
    z.f
    NormalX.f ; qu'avec l'option #PB_Mesh_Normal
    NormalY.f ;
    NormalZ.f ;
    TangentX.f
    TangentY.f
    TangentZ.f
    u.f       ; qu'avec l'option #PB_Mesh_UVCoordinate 
    v.f       ;
    Color.l   ; qu'avec l'option #PB_Mesh_Color
  EndStructure    
Cette structure peut être inclus dans un tableau ou bien dans une autre structure.

■ PB_MeshVertex dans un tableau et utilisation de copyArray
Avec ce code on crée deux entités (A et B) de tailles différentes puis à l'aide d'un GetMeshData() du mesh A on va exécuter un CopyArray() des informations du Mesh A vers le Mesh B pour obtenir la même taille.

Code : Tout sélectionner

Enumeration
  #Mainform
EndEnumeration

Define.l Event

Global Dim MeshDataA.PB_MeshVertex(0)
Global Dim MeshDataB.PB_MeshVertex(0)

InitEngine3D()
InitKeyboard()
InitSprite()
InitMouse()

OpenWindow(#Mainform,0,0,1024,768, "M", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(#Mainform),0,0,1024,768,0, 0, 0)

KeyboardMode(#PB_Keyboard_International)
 
;Lumiere et ombre
AmbientColor(RGB(127, 127, 127))
CreateLight(#PB_Any,RGB(151, 251, 151), -1.8, 10, 5)
WorldShadows(#PB_Shadow_Additive)

;Camera 
Camera = CreateCamera(#PB_Any,0,0,100,100)
CameraBackColor(Camera, RGB(145, 182, 201))
MoveCamera(Camera, 2, 5, 15, #PB_Absolute)  
CameraLookAt(Camera, 0,0,0)   

;Creation d'un mesh
MeshA = CreateCube(#PB_Any, 3)
MeshB = CreateCube(#PB_Any, 1)

;Création de deux entités de tailles différentes 
EntityA = CreateEntity(#PB_Any, MeshID(MeshA),#PB_Entity_None, -3, 0, 0)
EntityB = CreateEntity(#PB_Any, MeshID(MeshB),#PB_Entity_None, 3, 0, 0)

;Obtenir les données internes du Mesh A 
GetMeshData(MeshA, 0, MeshDataA(), #PB_Mesh_Vertex, 0, MeshVertexCount(MeshA)-1)

;Copie des données du Mesh A  vers le Mesh B
CopyArray(MeshDataA(), MeshDataB())

;Au final nous aurons deux meshs
SetMeshData(MeshB, 0, MeshDataB(), #PB_Mesh_Vertex, 0, MeshVertexCount(MeshB)-1)

Repeat
  Repeat
    Event  = WindowEvent()
    Select Event
      Case #PB_Event_CloseWindow
        End
        
    EndSelect
  Until Event = 0
  
  If ExamineKeyboard()
    
    If KeyboardPushed (#PB_Key_Escape)
      Break
    EndIf
  EndIf
  
  ; Affiche le rendu de la scène
  ClearScreen(RGB(0, 0, 0))
  RenderWorld(80)
  FlipBuffers()  
  
ForEver
■ PB_MeshVertex dans une structure

Code : Tout sélectionner

Enumeration
  #Mainform
EndEnumeration

Define.l Event

Structure Mesh
  Array Meshdata.PB_MeshVertex(0)
EndStructure

Global NewList Meshs.Mesh()

InitEngine3D()
InitKeyboard()
InitSprite()
InitMouse()

OpenWindow(#Mainform,0,0,1024,768, "M", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(#Mainform),0,0,1024,768,0, 0, 0)

KeyboardMode(#PB_Keyboard_International)
 
;Lumiere et ombre
AmbientColor(RGB(127, 127, 127))
CreateLight(#PB_Any,RGB(151, 251, 151), -1.8, 10, 5)
WorldShadows(#PB_Shadow_Additive)

;Camera 
Camera = CreateCamera(#PB_Any,0,0,100,100)
CameraBackColor(Camera, RGB(145, 182, 201))
MoveCamera(Camera, 2, 5, 15, #PB_Absolute)  
CameraLookAt(Camera, 0,0,0)   

;Creation de deux meshs de tailles différentes
MeshA = CreateCube(#PB_Any, 3)
MeshB = CreateCube(#PB_Any, 1)

;Création de deux entités de tailles différentes 
EntityA = CreateEntity(#PB_Any, MeshID(MeshA),#PB_Entity_None, -3, 0, 0)
EntityB = CreateEntity(#PB_Any, MeshID(MeshB),#PB_Entity_None, 3, 0, 0)

;Obtenir les données internes du Mesh A 
AddElement(Meshs())
GetMeshData(MeshA, 0, Meshs()\Meshdata(), #PB_Mesh_Vertex, 0, MeshVertexCount(MeshA)-1)

;Au final nous aurons deux meshs
SetMeshData(MeshB, 0, Meshs()\Meshdata(), #PB_Mesh_Vertex, 0, MeshVertexCount(MeshB)-1)

Repeat
  Repeat
    Event  = WindowEvent()
    Select Event
      Case #PB_Event_CloseWindow
        End
        
    EndSelect
  Until Event = 0
  
  If ExamineKeyboard()
    
    If KeyboardPushed (#PB_Key_Escape)
      Break
    EndIf
  EndIf
  
  ; Affiche le rendu de la scène
  ClearScreen(RGB(0, 0, 0))
  RenderWorld(80)
  FlipBuffers()  
  
ForEver
Pour copier un élément vers un autre on va créer un pointeur sur l'élément en cours et copier la zone de mémoire structurée courante vers la nouvelle.

Avec le code précédent, juste après

Code : Tout sélectionner

;Au final nous aurons deux meshs
SetMeshData(MeshB, 0, Meshs()\Meshdata(), #PB_Mesh_Vertex, 0, MeshVertexCount(MeshB)-1)
Ajoute

Code : Tout sélectionner

*Element = @Meshs()
AddElement(Meshs())
CopyStructure(*Element, @Meshs(), Mesh)
et tu pourras voir le résultat avec le visualiseur de variables.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

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

Message par kelebrindae »

Merci à vous deux pour ces compléments d'information!
Je ne sais pas comment je m'étais débrouillé, mais j'étais complémentent passé à côté de la syntaxe correcte pour déclarer le tableau dans la structure (comme quoi, certains jours, on est mal réveillé)...
J'avais fait:

Code : Tout sélectionner

Structure test
  tableau.i[50]
EndStructure
Define test1.test,test2.test

CopyArray(test1\tableau(),test2\tableau())
Alors que comme ça, ça marche:

Code : Tout sélectionner

Structure test
  Array tableau.i(50)
EndStructure
Define test1.test,test2.test

CopyArray(test1\tableau(),test2\tableau())
Apparemment, l'ancienne syntaxe est obsolète et ne permet pas d'exploiter pleinement les tableaux ainsi déclarés; peut-être faudrait-il mettre à jour l'aide en ligne concernant les structures...

Bon, prochaine étape: gagner de la mémoire en stockant les infos du terrain dans un octree plutôt que dans un tableau à trois dimensions (j'ai soudain compris le principe hier matin en sortant de chez moi; comme quoi, certains jours, je sais pourquoi je suis mal réveillé parce que visiblement mon cerveau avait continué de réfléchir au problème pendant la nuit.. :roll: ), et puis tenter de fluidifier le déroulement du programme en passant tout ce qui prend plus de 16ms dans un thread (16ms par boucle = 60fps).
Pour ce dernier point, je ne suis pas trop sûr de ce que ça va donner, mais on verra bien...
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
G-Rom
Messages : 3627
Inscription : dim. 10/janv./2010 5:29

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

Message par G-Rom »

Bon, prochaine étape: gagner de la mémoire en stockant les infos du terrain dans un octree plutôt que dans un tableau à trois dimensions (j'ai soudain compris le principe hier matin en sortant de chez moi; comme quoi, certains jours, je sais pourquoi je suis mal réveillé parce que visiblement mon cerveau avait continué de réfléchir au problème pendant la nuit.. :roll: ), et puis tenter de fluidifier le déroulement du programme en passant tout ce qui prend plus de 16ms dans un thread (16ms par boucle = 60fps).
Pour ce dernier point, je ne suis pas trop sûr de ce que ça va donner, mais on verra bien...
Tu vas te planté, un octree s'utilise en duo avec ton tableau à 3 Dimensions. L'octree est d'abord vidé de tout éléments. tu parcours ensuite ta liste de bloc dans ton tableau 3D , tu remplis ton octree, pour cela il faut connaitre la position du bloc à inséré dans le monde 3D. Tu l'insère dans l'octree racine que tu divises en 8, ce qui te donne 8 sous région plus petite, tu regarde dans laquelle de ces régions le bloc appartient, une fois défini tu l'insère dans la sous région. si la sous région est pleine ( admettons 20 bloc )
tu prends cette sous région & tu la redivise en 8, tu reprends les 20 bloc du dessus que tu fait descendre au enfants ( les sous régions ). ca , c'est la partie remplissage, il faut que tu définises des limites de profondeur et de taille de liste par noeud terminaux
ca ressemble un peu à cela :

Image
il manque des noeuds, c'est plus un quadtree là... mais le principe est le même.

Tout les blocs sont stocker dans des noeud terminaux, il n'y a pas de bloc présent dans les noeuds intermédiaire.

Une fois que tu as remplis ton octree , il faut que tu détermine la position de la camera dans l'octree , et définir ton "cone de vue", pour cela il y a le frustum culling, cela passe forcement par des calcul matriciel , il te faut la matrice de projection ( que tu ne peu pas avoir , et la matrice de vue, que tu ne peut pas avoir non plus avec les fct° PB ) et que tu calcul les plans de ta vue.
Une fois que tu as fait cela, tu fait une intersection entre tes plans et les boites de ton octree, les boite à l'interieur de tes plans sont donc visible, une simple fonction te permet de récupéré les blocs à afficher.

Je ne te cache pas que c'est quand même du lourd et qu'ogre le fait déjà en interne, ton travail sera doublon, je suppose que voudra passé par une solution "bricolage" , ca ne t'apportera rien en terme de performance de bricolé un octree, tu perdra même en performance. un octree bouffe plus de mémoire qu'un tableau brut 3D. de plus la mémoire dans un octree n'est pas contigüe. La solution que je t'ai donné plus haut est la plus approprié à mon avis, c'est par là que je creuserais. ;)

edit:

Grosso modo , voila la structure de base d'un octree :

Code : Tout sélectionner

Structure sNode
  List mBloc.i()
  mWidth.l
  mHeight.l
  *mChild.node[8]
  mIsLeaf.b
EndStructure

Structure sOctree
  mWidth.l
  mHeight.l
  *mRoot.sNode
EndStructure
A toi de rajouté les fonctions de découpage :D
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

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

Message par kelebrindae »

G-Rom a écrit :Tu vas te planté
M'enfin, je ne te permets pas!
Non, je déconne... :wink:

Peut-être qu'on ne parle pas de la même chose: l'octree ne servirait pas pour le culling, mais pour le stockage du monde. Et je voudrais me passer de tableau 3D en remplissant directement l'octree à partir du bruit de Perlin.

[Résumé, pour les autres lecteurs moins familiers du concept]
Le truc, avec un tableau(x,y,z), c'est qu'il a une taille fixe et qu'il occupe toujours la même taille en mémoire quel que soit son contenu. Or, le gros inconvénient des voxels, c'est la taille mémoire occupée par la map.
- Avec un stockage de type "Octree", on peut limiter les forts usages mémoire aux endroits où le monde est hétérogène, c'est-à-dire là où il y a plusieurs types de blocs.

Exemple, avec des chunks de 16x16x16:
Avec un tableau 3D, même si le chunk est vide, il occupe 16x16x16 = 4096 x [taille de bloc] espaces en mémoire.
- Avec un Octree, on peut juste dire que le noeud racine est uniformément rempli de la même chose (du vide ou autre) et c'est marre => occupation mémoire = 1 x taille d'un noeud.
S'il y a de la diversité (plusieurs types de blocs) dans un des huit sous-cubes, on créé la subdivision, on descend récursivement d'un cran, et on recommence. Au pire, avec des chunks de 16x16x16, on a 5 niveaux de récursion, ce qui reste raisonnable.
[Fin du résumé]

Alors c'est vrai que cela implique un coût algorithmique pour connaître le contenu d'un bloc ou le mettre à jour, mais ça économise beaucoup de mémoire, normalement. Et il y a sans doute des problèmes que je ne vois pas encore et que tu connais mieux que moi, c'est vrai. Mais je vais tenter le coup, ne serait-ce que pour ma culture personnelle (je suis têtu, j'en conviens :P ...).

Bref: rendez-vous au prochain message pour le compte-rendu de mes progrès (ou absence de progrès)!
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 »

Après avoir déplacé les appels aux procédures de création de mesh et d'ajout dans la géométrie statique vers une procédure "threadée", j'ai des plantages "invalid memory access" presque immédiats, soit sur "FinishMesh", soit sur "MeshVertexPosition", soit sur "BuildStaticGeometry".
Comme dans l'aide en ligne sur les threads, il est dit:
Don't use DirectX inside threads
je pense que c'est normal... :(

Quelqu'un pourrait me confirmer cette hypothèse? (Comtois ou G-Rom, par exemple)
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
G-Rom
Messages : 3627
Inscription : dim. 10/janv./2010 5:29

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

Message par G-Rom »

je confirme, c'est pas prévu pour. il faut voir un thread comme un "exécutable indépendant" avec comme parent , ton application.
il y a un problème donc de partage de ressources, de contexte, etc...
j'aurais dû te le dire avant :oops:
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

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

Message par kelebrindae »

Pas grave: en fait je m'en doutais car j'avais déjà lu l'aide en ligne avant de tester; je voulais juste en être sûr.
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

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

Message par graph100 »

kelebrindae a écrit :

Code : Tout sélectionner

Structure test
  tableau.i[50]
EndStructure
Define test1.test,test2.test

CopyArray(test1\tableau(),test2\tableau())
Alors que comme ça, ça marche:

Code : Tout sélectionner

Structure test
  Array tableau.i(50)
EndStructure
Define test1.test,test2.test

CopyArray(test1\tableau(),test2\tableau())
Apparemment, l'ancienne syntaxe est obsolète et ne permet pas d'exploiter pleinement les tableaux ainsi déclarés; peut-être faudrait-il mettre à jour l'aide en ligne concernant les structures...
Non, la première syntaxe désigne un tableau statique, que tu ne peux pas redimensionner.
C'est très utile pour récupérer des blobs de données d'un autre logiciel ou pour être compatible avec les code C++ etc...
Tu ne peux effectivement pas copier un tableau statique, il faut le copier en utilisant copymemory() dans le bon contexte
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
kelebrindae
Messages : 579
Inscription : ven. 11/mai/2007 15:21

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

Message par kelebrindae »

Ooooh, c'est donc ça...
Merci, Graph100; c'est une info très utile!
Les idées sont le souvenir de choses qui ne se sont pas encore produites.
Répondre