tree system work
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-10-14 09:01:44 -04:00
parent 5f54e5034c
commit a68e56285a
12 changed files with 658 additions and 309 deletions

View File

@ -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,

View File

@ -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",

Binary file not shown.

View File

@ -17,6 +17,10 @@
Add punching/unarmed combat
- Weapon raised/lowered component
Implement gadgets
- Chemistry System
- Emitters
- Subscribers
- Dedicated collision engine on server
- Trap
- Bear
- Freeze

View File

@ -861,6 +861,8 @@ Items executing script engine hooks on usage
Fix server attack tree regressions
Editing voxels hook and extensions for voxel palette item
Entity spawning palette item actually working via hooks
Model fix for shovel, shuffling item models
Work on separating procedural tree generation steps
# TODO

View File

@ -8,6 +8,7 @@ import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.entity.types.tree.ProceduralTree;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.foliage.type.ProceduralTreeBranchModel;
import electrosphere.game.data.foliage.type.TreeModel;
import electrosphere.game.data.graphics.ProceduralModel;
import electrosphere.renderer.actor.instance.InstancedActor;
@ -63,59 +64,62 @@ public class ImGuiEntityInstancedActorTab {
TreeModel treeModel = proceduralModel.getTreeModel();
maximumTrunkSegments[0] = treeModel.getMaximumTrunkSegments();
if(ImGui.sliderInt("maximumTrunkSegments", maximumTrunkSegments, 1, 8)){
treeModel.setMaximumTrunkSegments(maximumTrunkSegments[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
if(ImGui.collapsingHeader("Trunk")){
maximumTrunkSegments[0] = treeModel.getTrunkModel().getMaximumTrunkSegments();
if(ImGui.sliderInt("maximumTrunkSegments", maximumTrunkSegments, 1, 8)){
treeModel.getTrunkModel().setMaximumTrunkSegments(maximumTrunkSegments[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
}
if(ImGui.collapsingHeader("Branches")){
limbScalarFalloffFactor[0] = treeModel.getLimbScalarFalloffFactor();
ProceduralTreeBranchModel branchModel = treeModel.getBranchModel();
limbScalarFalloffFactor[0] = branchModel.getLimbScalarFalloffFactor();
if(ImGui.sliderFloat("limbScalarFalloffFactor", limbScalarFalloffFactor, 0.01f, 1f)){
treeModel.setLimbScalarFalloffFactor(limbScalarFalloffFactor[0]);
branchModel.setLimbScalarFalloffFactor(limbScalarFalloffFactor[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
minimumLimbScalar[0] = treeModel.getMinimumLimbScalar();
minimumLimbScalar[0] = branchModel.getMinimumLimbScalar();
if(ImGui.sliderFloat("minimumLimbScalar", minimumLimbScalar, 0.01f, 1f)){
treeModel.setLimbScalarFalloffFactor(minimumLimbScalar[0]);
branchModel.setLimbScalarFalloffFactor(minimumLimbScalar[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
maximumLimbDispersion[0] = treeModel.getMaximumLimbDispersion();
maximumLimbDispersion[0] = branchModel.getMaximumLimbDispersion();
if(ImGui.sliderFloat("maximumLimbDispersion", maximumLimbDispersion, 0.01f, 1f)){
treeModel.setMaximumLimbDispersion(maximumLimbDispersion[0]);
branchModel.setMaximumLimbDispersion(maximumLimbDispersion[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
minimumLimbDispersion[0] = treeModel.getMinimumLimbDispersion();
minimumLimbDispersion[0] = branchModel.getMinimumLimbDispersion();
if(ImGui.sliderFloat("minimumLimbDispersion", minimumLimbDispersion, 0.01f, 1f)){
treeModel.setMinimumLimbDispersion(minimumLimbDispersion[0]);
branchModel.setMinimumLimbDispersion(minimumLimbDispersion[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
minimumNumberForks[0] = treeModel.getMinimumNumberForks();
minimumNumberForks[0] = branchModel.getMinimumNumberForks();
if(ImGui.sliderInt("minimumNumberForks", minimumNumberForks, 1, 8)){
treeModel.setMinimumNumberForks(minimumNumberForks[0]);
branchModel.setMinimumNumberForks(minimumNumberForks[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
maximumNumberForks[0] = treeModel.getMaximumNumberForks();
maximumNumberForks[0] = branchModel.getMaximumNumberForks();
if(ImGui.sliderInt("maximumNumberForks", maximumNumberForks, 1, 8)){
treeModel.setMaximumNumberForks(maximumNumberForks[0]);
branchModel.setMaximumNumberForks(maximumNumberForks[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
maximumBranchSegments[0] = treeModel.getMaximumBranchSegments();
maximumBranchSegments[0] = branchModel.getMaximumBranchSegments();
if(ImGui.sliderInt("maximumBranchSegments", maximumBranchSegments, 1, 8)){
treeModel.setMaximumBranchSegments(maximumBranchSegments[0]);
branchModel.setMaximumBranchSegments(maximumBranchSegments[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
maxBranchSegmentFalloffFactor[0] = treeModel.getMaxBranchSegmentFalloffFactor();
maxBranchSegmentFalloffFactor[0] = branchModel.getMaxBranchSegmentFalloffFactor();
if(ImGui.sliderFloat("maxBranchSegmentFalloffFactor", maxBranchSegmentFalloffFactor, 0.01f, 1f)){
treeModel.setMaxBranchSegmentFalloffFactor(maxBranchSegmentFalloffFactor[0]);
branchModel.setMaxBranchSegmentFalloffFactor(maxBranchSegmentFalloffFactor[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}
}
@ -179,9 +183,11 @@ public class ImGuiEntityInstancedActorTab {
if(ImGui.collapsingHeader("Leaves")){
minimumSegmentToSpawnLeaves[0] = treeModel.getMinimumSegmentToSpawnLeaves();
ProceduralTreeBranchModel branchModel = treeModel.getBranchModel();
minimumSegmentToSpawnLeaves[0] = branchModel.getMinimumSegmentToSpawnLeaves();
if(ImGui.sliderInt("minimumSegmentToSpawnLeaves", minimumSegmentToSpawnLeaves, 1, 8)){
treeModel.setMinimumSegmentToSpawnLeaves(minimumSegmentToSpawnLeaves[0]);
branchModel.setMinimumSegmentToSpawnLeaves(minimumSegmentToSpawnLeaves[0]);
ImGuiEntityInstancedActorTab.regenerateModel(detailViewEntity,proceduralModel);
}

View File

@ -37,32 +37,44 @@ public class ScriptLevelEditorUtils {
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.selectedSpawntype.getId(), null);
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.selectedSpawntype.getId(), null);
}
} else if(Globals.selectedSpawntype instanceof Item){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.selectedSpawntype.getId());
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.selectedSpawntype.getId());
}
} else if(Globals.selectedSpawntype instanceof FoliageType){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.selectedSpawntype.getId(), new Random().nextLong());
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.selectedSpawntype.getId(), new Random().nextLong());
}
} else {
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.selectedSpawntype.getId());
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.selectedSpawntype.getId());
}
}
}

View File

@ -894,9 +894,9 @@ public class ControlHandler {
if(Globals.playerEntity != null && ClientToolbarState.getClientToolbarState(Globals.playerEntity) != null){
ClientToolbarState clientToolbarState = ClientToolbarState.getClientToolbarState(Globals.playerEntity);
if(scrollEvent.getScrollAmount() > 0){
clientToolbarState.attemptChangeSelection(clientToolbarState.getSelectedSlot() + 1);
} else {
clientToolbarState.attemptChangeSelection(clientToolbarState.getSelectedSlot() - 1);
} else {
clientToolbarState.attemptChangeSelection(clientToolbarState.getSelectedSlot() + 1);
}
}
}});

View File

@ -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<Entity> 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<Entity> 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<Entity> rVal = new LinkedList<Entity>();
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<Entity> clientGenerateBranches(
TreeModel type,
Entity parent,
Random rand,
TreeSegment segment
){
List<Entity> rVal = new LinkedList<Entity>();
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<Entity> childBranches = clientGenerateBranchesAlt(
List<Entity> 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
*/

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}