From a68e56285a6e35cd8585c20454a6fcf7b46190c0 Mon Sep 17 00:00:00 2001 From: austin Date: Mon, 14 Oct 2024 09:01:44 -0400 Subject: [PATCH] tree system work --- assets/Data/entity/foliage.json | 103 +++++- assets/Data/entity/items.json | 4 +- assets/Models/items/weapons/shovel1.glb | Bin 0 -> 9388 bytes docs/src/progress/currenttarget.md | 4 + docs/src/progress/renderertodo.md | 2 + .../entity/ImGuiEntityInstancedActorTab.java | 50 +-- .../menu/script/ScriptLevelEditorUtils.java | 28 +- .../controls/ControlHandler.java | 4 +- .../entity/types/tree/ProceduralTree.java | 338 ++++++++++++++---- .../type/ProceduralTreeBranchModel.java | 143 ++++++++ .../type/ProceduralTreeTrunkModel.java | 82 +++++ .../game/data/foliage/type/TreeModel.java | 209 ++--------- 12 files changed, 658 insertions(+), 309 deletions(-) create mode 100644 assets/Models/items/weapons/shovel1.glb create mode 100644 src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeBranchModel.java create mode 100644 src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeTrunkModel.java diff --git a/assets/Data/entity/foliage.json b/assets/Data/entity/foliage.json index f1717558..8b95f678 100644 --- a/assets/Data/entity/foliage.json +++ b/assets/Data/entity/foliage.json @@ -17,6 +17,79 @@ } } }, + { + "id" : "pine", + "tokens" : [ + "TREE", + "REACTS_TO_WIND", + "GROWS_BACK", + "FLAMMABLE", + "SEEDED" + ], + "growthModel": { + "growthRate" : 0.001 + }, + "ambientAudio": { + "responseWindAudioFilePath": "Audio/ambienceWind1SeamlessMono.ogg", + "responseWindLoops": true, + "randomizeOffset": true, + "gainMultiplier": 0.9, + "emitterSpatialOffset": [0,3,0] + }, + "graphicsTemplate": { + "proceduralModel": { + "treeModel": { + "trunkModel": { + "trunkScalarFalloffFactor": 0.2, + "minimumTrunkScalar": 0.2, + "maximumTrunkSegments": 6, + "physicsCutoff": 3, + "centralTrunk": true + }, + "branchModel": { + "limbScalarFalloffFactor": 0.3, + "minimumLimbScalar": 0.25, + "maximumLimbDispersion": 0.5, + "minimumLimbDispersion": 0.3, + "minimumNumberForks": 3, + "maximumNumberForks": 5, + "maximumBranchSegments": 3, + "maxBranchSegmentFalloffFactor": 1, + "minimumSegmentToSpawnLeaves": 2 + }, + "minBranchHeightToStartSpawningLeaves": 1.5, + "maxBranchHeightToStartSpawningLeaves": 3.01, + "leafIncrement": 0.5, + "minLeavesToSpawnPerPoint": 3, + "maxLeavesToSpawnPerPoint": 5, + "leafDistanceFromCenter": 1.2, + "peelVariance": 0.2, + "peelMinimum": 0.1, + "swaySigmoidFactor": 2, + "minimumSwayTime": 500, + "swayTimeVariance": 500, + "yawVariance": 0.2, + "yawMinimum": 0.1, + "minimumScalarToGenerateSwayTree": 0.5, + "maximumScalarToGenerateSwayTree": 1.0, + "branchHeight": 3, + "physicsBody": { + "type" : "CYLINDER", + "dimension1" : 0.5, + "dimension2" : 3, + "dimension3" : 0.5, + "rotX": 0, + "rotY": 0, + "rotZ": 0, + "rotW": 1, + "offsetX" : 0, + "offsetY" : 1.5, + "offsetZ" : 0 + } + } + } + } + }, { "id" : "oak", "tokens" : [ @@ -39,18 +112,22 @@ "graphicsTemplate": { "proceduralModel": { "treeModel": { - "limbScalarFalloffFactor": 0.3, - "minimumLimbScalar": 0.25, - "maximumLimbDispersion": 0.5, - "minimumLimbDispersion": 0.3, - "minimumNumberForks": 3, - "maximumNumberForks": 5, - "branchHeight": 3, - "centralTrunk": false, - "maximumTrunkSegments": 4, - "maximumBranchSegments": 3, - "maxBranchSegmentFalloffFactor": 1, - "minimumSegmentToSpawnLeaves": 2, + "trunkModel": { + "maximumTrunkSegments": 4, + "physicsCutoff": 3, + "centralTrunk": false + }, + "branchModel": { + "limbScalarFalloffFactor": 0.3, + "minimumLimbScalar": 0.25, + "maximumLimbDispersion": 0.5, + "minimumLimbDispersion": 0.3, + "minimumNumberForks": 3, + "maximumNumberForks": 5, + "maximumBranchSegments": 3, + "maxBranchSegmentFalloffFactor": 1, + "minimumSegmentToSpawnLeaves": 2 + }, "minBranchHeightToStartSpawningLeaves": 1.5, "maxBranchHeightToStartSpawningLeaves": 3.01, "leafIncrement": 0.5, @@ -66,7 +143,7 @@ "yawMinimum": 0.1, "minimumScalarToGenerateSwayTree": 0.5, "maximumScalarToGenerateSwayTree": 1.0, - "physicsCutoff": 3, + "branchHeight": 3, "physicsBody": { "type" : "CYLINDER", "dimension1" : 0.5, diff --git a/assets/Data/entity/items.json b/assets/Data/entity/items.json index 6a7fa830..d7bb9ac9 100644 --- a/assets/Data/entity/items.json +++ b/assets/Data/entity/items.json @@ -338,7 +338,7 @@ }, "graphicsTemplate": { "model": { - "path" : "Models/basic/geometry/unitvector.glb" + "path" : "Models/items/weapons/shovel1.glb" } }, "clientSidePrimary": "ADD_VOXEL", @@ -370,7 +370,7 @@ }, "graphicsTemplate": { "model": { - "path" : "Models/basic/geometry/unitcapsule.glb" + "path" : "Models/basic/geometry/unitvector.glb" } }, "clientSidePrimary": "LEVEL_EDIT_SPAWN", diff --git a/assets/Models/items/weapons/shovel1.glb b/assets/Models/items/weapons/shovel1.glb new file mode 100644 index 0000000000000000000000000000000000000000..df2e260f9a493961da826c1dd56b7c153c747d47 GIT binary patch literal 9388 zcmbtX3tW_C);}7lm5E|yg%_O6PzmP(Gs7_N`(WS&<6=OH;suc5lEa`dpx|&R&G1s1 z+GggaVw;yG({#;%0nAISo35L#ni-j0Y`1p(K0kH++;6}A&Uu`7co=wNzF*95&iOy* z{h$AH&Uv13#yor4gq{FUKLTKX0Knwb zqo^b#4^u*(vfCUvw&IZZvB@E25#hRUqd^mNp5^CzOC+B_R5QrLR2vX?g&9$Crzzz%uq z-kHRf+Y4x};aY7Z69NVlDRFbO8jBv!00j%?Qi`E{a+p)e$DK77AC(jp-x+0gX>P8qcxr)dAs3xn zR5-uLVRJas%ID*WMe6j%FwNYeQU~4*y+KcMi|F9OjXIOjV9@Igkp_d&ppC$3X?13! z&aBs(BJ>evQxtj%noLo~h)7d}Hp(1nHfbYhU&%f@ySRBIo|pgoo9Gsa>2zH>rW@#!a%UEv zIS9?0@p|Az4hgw94B)BwB)BxG+-=TFXgU>aXcTeIx_WsJ#+PAS4HG}J`ktmWRey;V zG4ao1d_Tt3F!9@rt(L*tO;!;T|4~D$MT>DYO#Fvmjn4KLvRRO;ypuW|KiP;|P&^<%!2^D`=}ZE3sQ3jX%Do26UNU6HTny};vL zVUa_4Q`H6e)X}uYW2;9uir6@0C_8v8t+5W{B7W<{-E8SkN!Ekx4M`2JFCV~mtvw&z z|5~l2hEomz*Sgg()pI>@o~6bmxz#Y$V}A8)!qK&ZBde`vF{&OH$6~z zMg9Tjd z#}||Sw>4%f_0=6#Y9PKZrhYg5cLmq!4pXke@x?KtpK)LG)YBcNT!rI{LsrGO58&?{ z>Z?0UxeCV@lfKq-Ufp4(2IBi-{I5EMFR{1$&(*`SfSU#E6R&BZXUZSuHDf-bwW!l| zyi=~MDtT%cnfzdHU@l-ky8voqEZ#dSOfOz%u!rtgTI!`~fZd>QeB$y5_v@pbl_c5^;TC)MY2W?HQggE zoNjB`dFrw(soNHbfY1+C42an9dbpEI9qWhVy-w7 zkuT0uoE4rm-qAaMB1hC0IbyERqbzd7xX`;Ka>TfR-6BWO=`cf)Bla%xMUL2u$QL=U zEX-^gGohUQ^P_xOz|EC-SO5LPB4)i{7vplpj6kX3yG1PIoV{7(3s~egpDyen&3Ut$ zEtzSNc5R7}R%BMQs3&J(zW^scu3%F?Ol%f4Zzr}&w)%25=+gxL@6V0WO7>D)VDs0B z?@0kmTr3W;FXzR5RqS}5lkOj-=?xW3 z-;yMYbJ$z3Px`~*a`w{IL_Bu@jkA*dIG{iNuHPx8uXeGru>P_yXa9vNwqV5}X|{Q- zG)!N`VjB-hBA;~rb6qt{A2v@CXC?B*IPsO*2rDxb>lEjyb}ni<6j%Bptju5R%Xj`= z@hMk17sARM#NK_+s4G6@DrZMnnUy#n@f_9o;tYMynsV?nLEm}wy8!=<=ij&9?>$=I z`K#gI-`#Ndcc;P;ze^Pr`uw$j4|{*t@cS-sT0FV@uBqwx!BOhr|K5pwVK1egpW~1Y z&4+V5^>gf_(;cUS4(X9@y9OceoRxaI;?R7&aS!&LA@hTy)YC#ctLb=p@5IrpIwY^l z4rHRQd3U*$*5q@F`ZDv5q3j5}!FlN!4f5yD2J&_5hFUAFLk87aPwwo)5{?aLyLYbP zJkb-6*5r>N?`HT&_N$ayuio&ffvgVKie78L3chbp;wX0O$Gw~<`j~Yqt+Xb8{m1}D zylcC6avu4o(*pUL^k^M&e7SY)iU;|=7pHCIJkb-6*5rvteJK4lS00xS&Ix3nVgK}; z){wfK?@M!Oiki;SgIr9?jbiH;+>#T*d$a7g;p|4qY*seZ<@LJH?qlm6Ls&u?yTemF z@-OTe$ubX3VD|@Bu+(Ma`E%m$SvHz&-yFlLG!yyvwS*znQ=00fQe8p3|Z9ynI8k5iUUpAY$KIzasi2u;Wjy$0E`hQ_< zvr9wS2Vae1pFVI_9(+#@J0Everggv2<6ckk!Rg2ObMl{$n8?KNAN zZ<{;$kX~ALbHh^E*Kf0#*p?gfWM6N8NMa-2nj!o8V<*P3rkl^mH%CrnJ3jlp-~OAh ziR_j7&9cA#eeFaxsdcIBuRpAx$Pym6%l`Tvcs}M2X2@#3`ZrVM?-Q5EBV$fWkXhpO zt0yMO8;0)YYtcJy#>*o@T=LqW>yqn>1#<48HaTS9A-QszmGdb+dFH!3zURt6Z!P~TE3?LN**!vfPAsn94YKfkxcy( zpW-!b)jUppQ@_NYv||nDDSqqC8N41^6HoCpPpT*V#9ryrUB%p!KJlqvir3D6mwU=v z01LqhAHXVj1??;FJiGx1payLXyb0A%4wYytVJj?xD%gT{3oM3BPzS4FGq_+0ybU{G zEo_IS@DltM-hebM%X0nh_Rz(}+s;VyUp`a=-f zAb1F3;b|C)b}WnnBSgUvv_s%w$b=Y(LmLNk;Zb-H2B94UgTVj;p�QpoL!e+oLDi zp3n!rg!AwP+ArV|d<$*xHQKM?3j7|9K`Yu;I1DY&0B*Ey_y|6OKfp<}C*jZVDIA4Q z(0&4cgpc76G~(+!_zGX2!)bh-!01J|3`fu&f#VoIjghbL-3aI4PjCk98MuIqgV2nx zBZ!8=BM^)>7(y`P6r9D^MMNLMH}E~$@1Y5-put|P_>O`nApo90`vfo;4fo(`G`^q2 zX*`S_8}V(xj{D&gExt#C1J5QKZ8q3o0z3twXhUHPW3Z8%JZRG11Tv?hpzX)psUXf0rdVkm=IXlFq>l)!XILz@Og@Eq7-3fd`< z2y;M&XtdFg0i}=w 0){ - clientToolbarState.attemptChangeSelection(clientToolbarState.getSelectedSlot() + 1); - } else { clientToolbarState.attemptChangeSelection(clientToolbarState.getSelectedSlot() - 1); + } else { + clientToolbarState.attemptChangeSelection(clientToolbarState.getSelectedSlot() + 1); } } }}); diff --git a/src/main/java/electrosphere/entity/types/tree/ProceduralTree.java b/src/main/java/electrosphere/entity/types/tree/ProceduralTree.java index 58526b18..609f7337 100644 --- a/src/main/java/electrosphere/entity/types/tree/ProceduralTree.java +++ b/src/main/java/electrosphere/entity/types/tree/ProceduralTree.java @@ -30,6 +30,8 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.attach.AttachUtils; import electrosphere.game.data.foliage.type.FoliageType; +import electrosphere.game.data.foliage.type.ProceduralTreeBranchModel; +import electrosphere.game.data.foliage.type.ProceduralTreeTrunkModel; import electrosphere.game.data.foliage.type.TreeModel; import electrosphere.renderer.actor.instance.InstancedActor; import electrosphere.renderer.buffer.ShaderAttribute; @@ -148,57 +150,188 @@ public class ProceduralTree { //generate branches - clientGenerateBranchesAlt( + clientGenerateTrunk( treeModel, trunkChild, treeRandom, - 0, - 0, - new Vector3d(0,0,0), - new Quaterniond(0,0,0,1), - new Vector3d(0,3,0), - new Quaternionf(0,0,0,1), - 1, - 1, - true + new TreeSegment( + 0, + 0, + new Vector3d(0,0,0), + new Quaterniond(0,0,0,1), + new Vector3d(0,3,0), + new Quaternionf(0,0,0,1), + 1, + 1, + true, + false + ) ); ClientEntityUtils.initiallyPositionEntity(trunkChild, EntityUtils.getPosition(trunkChild), EntityUtils.getRotation(trunkChild)); } - private static List clientGenerateBranchesAlt( + /** + * Generates trunk segments + * @param type The type of tree + * @param parent The parent entity + * @param rand The randomizer + * @param segment The current segment + * @return The entities that were generated + */ + private static List clientGenerateTrunk( TreeModel type, Entity parent, Random rand, - double parentPeel, - double parentRotationOffset, - Vector3d parentPosition, // The parent's origin bone's position in space - Quaterniond parentRotation, // The parent's origin bone's rotation - Vector3d offsetFromParent, // The offset from the parent's origin bone that this branch's origin bone should be at - Quaternionf rotationFromParent, // The rotation of the parent's extended bone. Should be equivalent to the origin bone's rotation on this branch - float scalar, - int currentSegmentNumber, - boolean isCentralTrunk + TreeSegment segment ){ List rVal = new LinkedList(); + + ProceduralTreeTrunkModel trunkModel = type.getTrunkModel(); + ProceduralTreeBranchModel branchModel = type.getBranchModel(); + + //spawn trunk segments + if( + trunkModel.getCentralTrunk() && + segment.scalar > trunkModel.getMinimumTrunkScalar() && + segment.currentSegmentNumber < trunkModel.getMaximumTrunkSegments() + ){ + //what we want to solve for: + //get parent position + rotation + //get an offset from the parent position for this position + //get a rotation from the parent rotation for this rotation + //get an offset from the current position for the child position + //get a rotation from the current rotation for the child rotation + + //calculate transform from parent entity + //this is the transform that will be applied every time the attachutils updates + Matrix4f transformFromParent = new Matrix4f() + .translate(new Vector3f( + (float)segment.offsetFromParent.x, + (float)segment.offsetFromParent.y, + (float)segment.offsetFromParent.z + )) + .rotate(new Quaternionf( + (float)segment.rotationFromParent.x, + (float)segment.rotationFromParent.y, + (float)segment.rotationFromParent.z, + (float)segment.rotationFromParent.w + )); + + //calculate combined transform + Matrix4f combinedTransform = new Matrix4f().translate(new Vector3f( + (float)segment.parentPosition.x, + (float)segment.parentPosition.y, + (float)segment.parentPosition.z + )).rotate(new Quaternionf( + (float)segment.parentRotation.x, + (float)segment.parentRotation.y, + (float)segment.parentRotation.z, + (float)segment.parentRotation.w + )).mul(transformFromParent); + + + + + + + + //calculate current branch's stuff + //get current position + Vector4f currentPositionf = combinedTransform.transform(new Vector4f( + 0, + 0, + 0, + 1 + )); + Vector3d currentPosition = new Vector3d(currentPositionf.x,currentPositionf.y,currentPositionf.z); + + //The new absolute rotation at the end of the bone + Quaterniond currentAbsoluteRotation = combinedTransform.getNormalizedRotation(new Quaterniond()).normalize(); + + //the rotation applied to the bone + Quaternionf boneRotation = new Quaternionf(0,0,0,1); + + //calculates the bone transform matrix + Matrix4f boneTransform = new Matrix4f().identity().rotate(boneRotation); + + //new position transform + Matrix4f newPositionTransform = new Matrix4f().rotate(boneRotation).translate(0,type.getBranchHeight(),0); + + Vector4f newPositionRaw = newPositionTransform.transform(new Vector4f(0,0,0,1)); + Vector3d newPosition = new Vector3d(newPositionRaw.x,newPositionRaw.y,newPositionRaw.z); + + //get new scalar + float newScalar = segment.scalar - trunkModel.getTrunkScalarFalloffFactor(); + + //create entity + + Entity trunkSegment = EntityCreationUtils.createClientSpatialEntity(); + InstancedActor instancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(trunkSegment, branchInstanceTemplate, modelMatrixAttribute); + instancedActor.setAttribute(boneMatrixAttribute, boneTransform.scale(newScalar,1,newScalar)); + instancedActor.setAttribute(baseSizeAttribute, segment.scalar); + instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity()); + + //set entity stuuff + EntityUtils.getPosition(trunkSegment).set(currentPosition); + EntityUtils.getScale(trunkSegment).set(1,1,1); + EntityUtils.getRotation(trunkSegment).set(currentAbsoluteRotation); + // AttachUtils.clientAttachEntityAtCurrentOffset(parent, branch); + AttachUtils.clientAttachEntityAtTransform(parent, trunkSegment, transformFromParent); + + rVal.add(trunkSegment); + clientGenerateTrunk( + type, + parent, + rand, + new TreeSegment( + 0, + 0, + currentPosition, + currentAbsoluteRotation, + newPosition, + boneRotation, + segment.scalar - trunkModel.getTrunkScalarFalloffFactor(), + segment.currentSegmentNumber + 1, + false, //can't be central trunk + false + ) + ); + } + + //spawn branches + if(segment.scalar > branchModel.getMinimumLimbScalar() && segment.currentSegmentNumber < branchModel.getMaximumBranchSegments()){ + clientGenerateBranches(type,parent,rand,segment); + } + return rVal; + } + + private static List clientGenerateBranches( + TreeModel type, + Entity parent, + Random rand, + TreeSegment segment + ){ + List rVal = new LinkedList(); + + ProceduralTreeBranchModel branchModel = type.getBranchModel(); //how fast do the branches shrink in size - float scalarFalloffFactor = type.getLimbScalarFalloffFactor(); + float scalarFalloffFactor = branchModel.getLimbScalarFalloffFactor(); //the minimum branch size before we stop generating branch segments/trunk segments - float minimumScalar = type.getMinimumLimbScalar(); + float minimumScalar = branchModel.getMinimumLimbScalar(); //how high is the model for a single branch segment float treeSegmentHeight = type.getBranchHeight(); //how much to spread the branches along the current segment - float minimumSegmentDispersion = type.getMinimumLimbDispersion(); - float dispersionSpread = type.getMaximumLimbDispersion() - type.getMinimumLimbDispersion(); + float minimumSegmentDispersion = branchModel.getMinimumLimbDispersion(); + float dispersionSpread = branchModel.getMaximumLimbDispersion() - branchModel.getMinimumLimbDispersion(); //the number of branches to make per segment - int minBranches = type.getMinimumNumberForks(); - int maxBranches = type.getMaximumNumberForks(); + int minBranches = branchModel.getMinimumNumberForks(); + int maxBranches = branchModel.getMaximumNumberForks(); //the maximum number of segments in an single arc for both trunk and branches - int maximumTrunkSegments = type.getMaximumTrunkSegments(); - int maximumBranchSegments = type.getMaximumBranchSegments(); + int maximumBranchSegments = branchModel.getMaximumBranchSegments(); - if(scalar > minimumScalar && currentSegmentNumber < maximumBranchSegments){ - int minimumSegmentToSpawnLeaves = type.getMinimumSegmentToSpawnLeaves(); + if(segment.scalar > minimumScalar && segment.currentSegmentNumber < maximumBranchSegments){ + int minimumSegmentToSpawnLeaves = branchModel.getMinimumSegmentToSpawnLeaves(); //how much does it peel off of the current vector double peelRotation = (rand.nextFloat() * dispersionSpread + minimumSegmentDispersion); @@ -219,27 +352,27 @@ public class ProceduralTree { //this is the transform that will be applied every time the attachutils updates Matrix4f transformFromParent = new Matrix4f() .translate(new Vector3f( - (float)offsetFromParent.x, - (float)offsetFromParent.y, - (float)offsetFromParent.z + (float)segment.offsetFromParent.x, + (float)segment.offsetFromParent.y, + (float)segment.offsetFromParent.z )) .rotate(new Quaternionf( - (float)rotationFromParent.x, - (float)rotationFromParent.y, - (float)rotationFromParent.z, - (float)rotationFromParent.w + (float)segment.rotationFromParent.x, + (float)segment.rotationFromParent.y, + (float)segment.rotationFromParent.z, + (float)segment.rotationFromParent.w )); //calculate combined transform Matrix4f combinedTransform = new Matrix4f().translate(new Vector3f( - (float)parentPosition.x, - (float)parentPosition.y, - (float)parentPosition.z + (float)segment.parentPosition.x, + (float)segment.parentPosition.y, + (float)segment.parentPosition.z )).rotate(new Quaternionf( - (float)parentRotation.x, - (float)parentRotation.y, - (float)parentRotation.z, - (float)parentRotation.w + (float)segment.parentRotation.x, + (float)segment.parentRotation.y, + (float)segment.parentRotation.z, + (float)segment.parentRotation.w )).mul(transformFromParent); @@ -291,7 +424,7 @@ public class ProceduralTree { Vector3d newPosition = new Vector3d(newPositionRaw.x,newPositionRaw.y,newPositionRaw.z); //get new scalar - float newScalar = scalar - scalarFalloffFactor; + float newScalar = segment.scalar - scalarFalloffFactor; @@ -303,7 +436,7 @@ public class ProceduralTree { Entity branch = EntityCreationUtils.createClientSpatialEntity(); InstancedActor instancedActor = InstancedEntityUtils.makeEntityInstancedWithModelTransform(branch, branchInstanceTemplate, modelMatrixAttribute); instancedActor.setAttribute(boneMatrixAttribute, boneTransform.scale(newScalar,1,newScalar)); - instancedActor.setAttribute(baseSizeAttribute, scalar); + instancedActor.setAttribute(baseSizeAttribute, segment.scalar); instancedActor.setAttribute(modelMatrixAttribute, new Matrix4f().identity()); //set entity stuuff @@ -321,20 +454,25 @@ public class ProceduralTree { //attach leaf blobs if( - !isCentralTrunk && - currentSegmentNumber >= minimumSegmentToSpawnLeaves + !segment.isTrunk && + segment.currentSegmentNumber >= minimumSegmentToSpawnLeaves ){ createLeafBlobsOnBranch( type, branch, rand, - currentPosition, - currentAbsoluteRotation, - newPosition, - boneRotation, - scalar - scalarFalloffFactor, - currentSegmentNumber + 1, - false //can't be central trunk + new TreeSegment( + peelRotation, + offsetRotation, + currentPosition, + currentAbsoluteRotation, + newPosition, + boneRotation, + segment.scalar - scalarFalloffFactor, + segment.currentSegmentNumber + 1, + false, //can't be central trunk + false + ) ); } @@ -342,23 +480,26 @@ public class ProceduralTree { //recurse - List childBranches = clientGenerateBranchesAlt( + List childBranches = clientGenerateBranches( type, branch, rand, - peelRotation, - offsetRotation, - currentPosition, - currentAbsoluteRotation, - newPosition, - boneRotation, - scalar - scalarFalloffFactor, - currentSegmentNumber + 1, - false //can't be central trunk + new TreeSegment( + peelRotation, + offsetRotation, + currentPosition, + currentAbsoluteRotation, + newPosition, + boneRotation, + segment.scalar - scalarFalloffFactor, + segment.currentSegmentNumber + 1, + false, //can't be central trunk + false + ) ); //add behavior tree to update all child branch attachment points to new point - if(childBranches.size() > 0 && scalar >= type.getMinimumScalarToGenerateSwayTree() && scalar <= type.getMaximumScalarToGenerateSwayTree()){ + if(childBranches.size() > 0 && segment.scalar >= type.getMinimumScalarToGenerateSwayTree() && segment.scalar <= type.getMaximumScalarToGenerateSwayTree()){ Globals.clientSceneWrapper.getScene().registerBehaviorTree(new BranchBehaviorTree( type, parent, @@ -647,13 +788,14 @@ public class ProceduralTree { TreeModel type, Entity parent, Random rand, - Vector3d parentPosition, // The parent's origin bone's position in space - Quaterniond parentRotation, // The parent's origin bone's rotation - Vector3d offsetFromParent, // The offset from the parent's origin bone that this branch's origin bone should be at - Quaternionf rotationFromParent, // The rotation of the parent's extended bone. Should be equivalent to the origin bone's rotation on this branch - float scalar, - int currentSegmentNumber, - boolean isCentralTrunk + TreeSegment segment + // Vector3d parentPosition, // The parent's origin bone's position in space + // Quaterniond parentRotation, // The parent's origin bone's rotation + // Vector3d offsetFromParent, // The offset from the parent's origin bone that this branch's origin bone should be at + // Quaternionf rotationFromParent, // The rotation of the parent's extended bone. Should be equivalent to the origin bone's rotation on this branch + // float scalar, + // int currentSegmentNumber, + // boolean isCentralTrunk ){ //get type data float minBranchHeightToStartSpawningLeaves = type.getMinBranchHeightToStartSpawningLeaves(); @@ -684,7 +826,7 @@ public class ProceduralTree { currentLeafRotation = currentLeafRotation + (leafIncrementer + 1) * 2.0 * Math.PI / (float)numToSpawn; //calculate the transform from parent - Quaterniond parentLocalRotationFromVertical = new Quaterniond().rotationTo(new Vector3d(0,1,0), offsetFromParent); + Quaterniond parentLocalRotationFromVertical = new Quaterniond().rotationTo(new Vector3d(0,1,0), segment.offsetFromParent); Vector4d transformedPos = parentLocalRotationFromVertical.transform(new Vector4d(xOffset,positionAlongBranch,zOffset,1)); //calculate transform from parent entity @@ -696,10 +838,10 @@ public class ProceduralTree { (float)transformedPos.z )) .rotate(new Quaternionf( - (float)rotationFromParent.x, - (float)rotationFromParent.y, - (float)rotationFromParent.z, - (float)rotationFromParent.w + (float)segment.rotationFromParent.x, + (float)segment.rotationFromParent.y, + (float)segment.rotationFromParent.z, + (float)segment.rotationFromParent.w )); @@ -828,6 +970,46 @@ public class ProceduralTree { // } } + /** + * A segment in a tree that is being generated + */ + static class TreeSegment { + protected double parentPeel; + protected double parentRotationOffset; + protected Vector3d parentPosition; // The parent's origin bone's position in space + protected Quaterniond parentRotation; // The parent's origin bone's rotation + protected Vector3d offsetFromParent; // The offset from the parent's origin bone that this branch's origin bone should be at + protected Quaternionf rotationFromParent; // The rotation of the parent's extended bone. Should be equivalent to the origin bone's rotation on this branch + protected float scalar; //the current size scalar + protected int currentSegmentNumber; //the current segment number + protected boolean isTrunk; //true if this is a trunk segment + protected boolean isBrandAdapter; //true if this adapts from the trunk to a position on the trunk to branch from + + public TreeSegment( + double parentPeel, + double parentRotationOffset, + Vector3d parentPosition, // The parent's origin bone's position in space + Quaterniond parentRotation, // The parent's origin bone's rotation + Vector3d offsetFromParent, // The offset from the parent's origin bone that this branch's origin bone should be at + Quaternionf rotationFromParent, // The rotation of the parent's extended bone. Should be equivalent to the origin bone's rotation on this branch + float scalar, //the current size scalar + int currentSegmentNumber, //the current segment number + boolean isTrunk, //true if this is a trunk segment + boolean isBrandAdapter //true if this adapts from the trunk to a position on the trunk to branch from + ){ + this.parentPeel = parentPeel; + this.parentRotationOffset = parentRotationOffset; + this.parentPosition = parentPosition; + this.parentRotation = parentRotation; + this.offsetFromParent = offsetFromParent; + this.rotationFromParent = rotationFromParent; + this.scalar = scalar; + this.currentSegmentNumber = currentSegmentNumber; + this.isTrunk = isTrunk; + this.isBrandAdapter = isBrandAdapter; + } + } + /** * The behavior tree for branches swaying in the wind */ diff --git a/src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeBranchModel.java b/src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeBranchModel.java new file mode 100644 index 00000000..0fcd0689 --- /dev/null +++ b/src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeBranchModel.java @@ -0,0 +1,143 @@ +package electrosphere.game.data.foliage.type; + +/** + * Data for creating branches on a procedural tree + */ +public class ProceduralTreeBranchModel { + + //how quickly do the limbs shrink + float limbScalarFalloffFactor; + + //How small are the terminal limbs, basically how small can it get before it stops generating + float minimumLimbScalar; + + //The maximum a single branch can disperse from the current line + float maximumLimbDispersion; + + //The minimum a single branch must disperse from the current line + float minimumLimbDispersion; + + //The minimum number of branch forks per iteration + int minimumNumberForks; + + //The maximum number of branch forks per iteration + int maximumNumberForks; + + //The maximum number of linear segments for the branch (ie how many times can a function recurse) + int maximumBranchSegments; + + //The rate at which number of branch segments from the current trunk falls off over time + float maxBranchSegmentFalloffFactor; + + //The minimum segment number required to start spawning leaves + int minimumSegmentToSpawnLeaves; + + /** + * how quickly do the limbs shrink + * @return + */ + public float getLimbScalarFalloffFactor(){ + return limbScalarFalloffFactor; + } + + /** + * How small are the terminal limbs + * @return + */ + public float getMinimumLimbScalar(){ + return minimumLimbScalar; + } + + /** + * The maximum a single branch can disperse from the current line + * @return + */ + public float getMaximumLimbDispersion(){ + return maximumLimbDispersion; + } + + /** + * The minimum a single branch must disperse from the current line + * @return + */ + public float getMinimumLimbDispersion(){ + return minimumLimbDispersion; + } + + /** + * The minimum number of branch forks per iteration + * @return + */ + public int getMinimumNumberForks(){ + return minimumNumberForks; + } + + /** + * The maximum number of branch forks per iteration + * @return + */ + public int getMaximumNumberForks(){ + return maximumNumberForks; + } + + /** + * The maximum number of linear segments for the branch (ie how many times can a function recurse) + * @return + */ + public int getMaximumBranchSegments(){ + return maximumBranchSegments; + } + + /** + * The rate at which number of branch segments from the current trunk falls off over time + * @return + */ + public float getMaxBranchSegmentFalloffFactor(){ + return maxBranchSegmentFalloffFactor; + } + + /** + * The minimum segment number required to start spawning leaves + * @return + */ + public int getMinimumSegmentToSpawnLeaves(){ + return minimumSegmentToSpawnLeaves; + } + + public void setLimbScalarFalloffFactor(float limbScalarFalloffFactor) { + this.limbScalarFalloffFactor = limbScalarFalloffFactor; + } + + public void setMinimumLimbScalar(float minimumLimbScalar) { + this.minimumLimbScalar = minimumLimbScalar; + } + + public void setMaximumLimbDispersion(float maximumLimbDispersion) { + this.maximumLimbDispersion = maximumLimbDispersion; + } + + public void setMinimumLimbDispersion(float minimumLimbDispersion) { + this.minimumLimbDispersion = minimumLimbDispersion; + } + + public void setMinimumNumberForks(int minimumNumberForks) { + this.minimumNumberForks = minimumNumberForks; + } + + public void setMaximumNumberForks(int maximumNumberForks) { + this.maximumNumberForks = maximumNumberForks; + } + + public void setMaximumBranchSegments(int maximumBranchSegments) { + this.maximumBranchSegments = maximumBranchSegments; + } + + public void setMaxBranchSegmentFalloffFactor(float maxBranchSegmentFalloffFactor) { + this.maxBranchSegmentFalloffFactor = maxBranchSegmentFalloffFactor; + } + + public void setMinimumSegmentToSpawnLeaves(int minimumSegmentToSpawnLeaves) { + this.minimumSegmentToSpawnLeaves = minimumSegmentToSpawnLeaves; + } + +} diff --git a/src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeTrunkModel.java b/src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeTrunkModel.java new file mode 100644 index 00000000..c90a163f --- /dev/null +++ b/src/main/java/electrosphere/game/data/foliage/type/ProceduralTreeTrunkModel.java @@ -0,0 +1,82 @@ +package electrosphere.game.data.foliage.type; + +/** + * The data model for the trunk of the procedural tree + */ +public class ProceduralTreeTrunkModel { + + //how quickly does the trunk shrink + float trunkScalarFalloffFactor; + + //How small are the trunk segments, basically how small can it get before it stops generating + float minimumTrunkScalar; + + //The minimum number of branch forks per iteration + int minimumBranches; + + //The maximum number of branch forks per iteration + int maximumBranches; + + //if true, always generates a central trunk + boolean centralTrunk; + + //The maximum number of linear segments for the trunk (ie how many times can a function recurse) + int maximumTrunkSegments; + + //The branch count to stop generating physics for each branch + int physicsCutoff; + + + /** + * Gets the scalar falloff factor (how fast does the trunk shrink) + * @return The scalar falloff factor + */ + public float getTrunkScalarFalloffFactor(){ + return trunkScalarFalloffFactor; + } + + /** + * Gets the minimum scalar for a trunk segment + * @return The minimum scalar + */ + public float getMinimumTrunkScalar(){ + return minimumTrunkScalar; + } + + /** + * if true, always generates a central trunk + * @return + */ + public boolean getCentralTrunk(){ + return centralTrunk; + } + + /** + * The maximum number of linear segments for the trunk (ie how many times can a function recurse) + * @return + */ + public int getMaximumTrunkSegments(){ + return maximumTrunkSegments; + } + + /** + * The branch count to stop generating physics for each branch + * @return + */ + public int getPhysicsCutoff(){ + return this.physicsCutoff; + } + + public void setCentralTrunk(boolean centralTrunk) { + this.centralTrunk = centralTrunk; + } + + public void setMaximumTrunkSegments(int maximumTrunkSegments) { + this.maximumTrunkSegments = maximumTrunkSegments; + } + + public void setPhysicsCutoff(int physicsCutoff) { + this.physicsCutoff = physicsCutoff; + } + +} diff --git a/src/main/java/electrosphere/game/data/foliage/type/TreeModel.java b/src/main/java/electrosphere/game/data/foliage/type/TreeModel.java index f104492e..4dd4e9bb 100644 --- a/src/main/java/electrosphere/game/data/foliage/type/TreeModel.java +++ b/src/main/java/electrosphere/game/data/foliage/type/TreeModel.java @@ -6,42 +6,12 @@ import electrosphere.game.data.collidable.CollidableTemplate; * Describes characteristics about a type of tree (how do the limbs dispere, where to the leaves start growing, how sturdy is it, etc) */ public class TreeModel { - - //how quickly do the limbs shrink - float limbScalarFalloffFactor; - - //How small are the terminal limbs, basically how small can it get before it stops generating - float minimumLimbScalar; - //The maximum a single branch can disperse from the current line - float maximumLimbDispersion; + //trunk data + ProceduralTreeTrunkModel trunkModel; - //The minimum a single branch must disperse from the current line - float minimumLimbDispersion; - - //The minimum number of branch forks per iteration - int minimumNumberForks; - - //The maximum number of branch forks per iteration - int maximumNumberForks; - - //The height of a single branch, should be the height of the model - float branchHeight; - - //if true, always generates a central trunk - boolean centralTrunk; - - //The maximum number of linear segments for the trunk (ie how many times can a function recurse) - int maximumTrunkSegments; - - //The maximum number of linear segments for the branch (ie how many times can a function recurse) - int maximumBranchSegments; - - //The rate at which number of branch segments from the current trunk falls off over time - float maxBranchSegmentFalloffFactor; - - //The minimum segment number required to start spawning leaves - int minimumSegmentToSpawnLeaves; + //branch data + ProceduralTreeBranchModel branchModel; //the minimum distance along a given segment to start spawning leaves at float minBranchHeightToStartSpawningLeaves; @@ -61,9 +31,6 @@ public class TreeModel { //The distance from the central line of a branch to spawn a leaf at float leafDistanceFromCenter; - //The branch count to stop generating physics for each branch - int physicsCutoff; - //The rigid body definition for a full scale tree branch CollidableTemplate physicsBody; @@ -105,100 +72,23 @@ public class TreeModel { //the model for a leaf blob String leafModelPath; + //The height of a single branch, should be the height of the model + float branchHeight; + /** - * how quickly do the limbs shrink - * @return + * Gets the data model for the trunk + * @return The data model for the trunk */ - public float getLimbScalarFalloffFactor(){ - return limbScalarFalloffFactor; + public ProceduralTreeTrunkModel getTrunkModel(){ + return trunkModel; } /** - * How small are the terminal limbs - * @return + * Gets the data model for branches + * @return The data model for branches */ - public float getMinimumLimbScalar(){ - return minimumLimbScalar; - } - - /** - * The maximum a single branch can disperse from the current line - * @return - */ - public float getMaximumLimbDispersion(){ - return maximumLimbDispersion; - } - - /** - * The minimum a single branch must disperse from the current line - * @return - */ - public float getMinimumLimbDispersion(){ - return minimumLimbDispersion; - } - - /** - * The minimum number of branch forks per iteration - * @return - */ - public int getMinimumNumberForks(){ - return minimumNumberForks; - } - - /** - * The maximum number of branch forks per iteration - * @return - */ - public int getMaximumNumberForks(){ - return maximumNumberForks; - } - - /** - * The height of a single branch, should be the height of the model - * @return - */ - public float getBranchHeight(){ - return branchHeight; - } - - /** - * if true, always generates a central trunk - * @return - */ - public boolean getCentralTrunk(){ - return centralTrunk; - } - - /** - * The maximum number of linear segments for the trunk (ie how many times can a function recurse) - * @return - */ - public int getMaximumTrunkSegments(){ - return maximumTrunkSegments; - } - - /** - * The maximum number of linear segments for the branch (ie how many times can a function recurse) - * @return - */ - public int getMaximumBranchSegments(){ - return maximumBranchSegments; - } - - /** - * The rate at which number of branch segments from the current trunk falls off over time - * @return - */ - public float getMaxBranchSegmentFalloffFactor(){ - return maxBranchSegmentFalloffFactor; - } - - /** - * The minimum segment number required to start spawning leaves - * @return - */ - public int getMinimumSegmentToSpawnLeaves(){ - return minimumSegmentToSpawnLeaves; + public ProceduralTreeBranchModel getBranchModel(){ + return branchModel; } /** @@ -321,15 +211,6 @@ public class TreeModel { return this.maximumScalarToGenerateSwayTree; } - - /** - * The branch count to stop generating physics for each branch - * @return - */ - public int getPhysicsCutoff(){ - return this.physicsCutoff; - } - /** * The rigid body definition for a full scale tree branch * @return @@ -354,52 +235,12 @@ public class TreeModel { return leafModelPath; } - public void setLimbScalarFalloffFactor(float limbScalarFalloffFactor) { - this.limbScalarFalloffFactor = limbScalarFalloffFactor; - } - - public void setMinimumLimbScalar(float minimumLimbScalar) { - this.minimumLimbScalar = minimumLimbScalar; - } - - public void setMaximumLimbDispersion(float maximumLimbDispersion) { - this.maximumLimbDispersion = maximumLimbDispersion; - } - - public void setMinimumLimbDispersion(float minimumLimbDispersion) { - this.minimumLimbDispersion = minimumLimbDispersion; - } - - public void setMinimumNumberForks(int minimumNumberForks) { - this.minimumNumberForks = minimumNumberForks; - } - - public void setMaximumNumberForks(int maximumNumberForks) { - this.maximumNumberForks = maximumNumberForks; - } - - public void setBranchHeight(float branchHeight) { - this.branchHeight = branchHeight; - } - - public void setCentralTrunk(boolean centralTrunk) { - this.centralTrunk = centralTrunk; - } - - public void setMaximumTrunkSegments(int maximumTrunkSegments) { - this.maximumTrunkSegments = maximumTrunkSegments; - } - - public void setMaximumBranchSegments(int maximumBranchSegments) { - this.maximumBranchSegments = maximumBranchSegments; - } - - public void setMaxBranchSegmentFalloffFactor(float maxBranchSegmentFalloffFactor) { - this.maxBranchSegmentFalloffFactor = maxBranchSegmentFalloffFactor; - } - - public void setMinimumSegmentToSpawnLeaves(int minimumSegmentToSpawnLeaves) { - this.minimumSegmentToSpawnLeaves = minimumSegmentToSpawnLeaves; + /** + * The height of a single branch, should be the height of the model + * @return + */ + public float getBranchHeight(){ + return branchHeight; } public void setMinBranchHeightToStartSpawningLeaves(float minBranchHeightToStartSpawningLeaves) { @@ -426,10 +267,6 @@ public class TreeModel { this.leafDistanceFromCenter = leafDistanceFromCenter; } - public void setPhysicsCutoff(int physicsCutoff) { - this.physicsCutoff = physicsCutoff; - } - public void setPhysicsBody(CollidableTemplate physicsBody) { this.physicsBody = physicsBody; } @@ -478,6 +315,10 @@ public class TreeModel { this.leafModelPath = leafModelPath; } + public void setBranchHeight(float branchHeight) { + this.branchHeight = branchHeight; + } + }