Skip to content

Commit

Permalink
Merge pull request #148 from JamesTKhan/scenegraph-search
Browse files Browse the repository at this point in the history
Add convenience search methods to SceneGraph
  • Loading branch information
JamesTKhan authored Apr 19, 2023
2 parents 8756b06 + 4912ee6 commit 371b8ef
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,74 @@ public void isComponentAddable(Component component) throws InvalidComponentExcep
}
}

/**
* Returns the first child GameObject matching the name.
*
* @param name the GameObject name to search for
* @return the first GameObject found or null if not found
*/
public GameObject findChildByName(String name) {
for (GameObject go : this) {
if (go.name.equals(name)) {
return go;
}
}

return null;
}

/**
* Returns an Array of all child GameObjects matching the name.
*
* @param name the GameObject name to search for
* @return Array of all matching GameObjects
*/
public Array<GameObject> findChildrenByName(String name) {
Array<GameObject> objects = new Array<>();
for (GameObject go : this) {
if (go.name.equals(name)) {
objects.add(go);
}
}

return objects;
}

/**
* Returns an Array of all child GameObjects that have the given Component.Type
*
* @param type the Component Type to search for
* @return Array of all matching GameObjects
*/
public Array<GameObject> findChildrenByComponent(Component.Type type) {
Array<GameObject> objects = new Array<>();
for (GameObject go : this) {
Component component = go.findComponentByType(type);
if (component != null) {
objects.add(go);
}
}

return objects;
}

/**
* Returns an Array of all child GameObjects that have the given Tag
*
* @param tag the string tag to search for
* @return Array of all matching GameObjects
*/
public Array<GameObject> findChildrenByTag(String tag) {
Array<GameObject> objects = new Array<>();
for (GameObject go : this) {
if (go.tags != null && go.tags.contains(tag, false)) {
objects.add(go);
}
}

return objects;
}

@Override
public void addChild(GameObject child) {
super.addChild(child);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,49 @@ private int getNextId() {
return maxId + 1;
}

/**
* Returns the first GameObject in the scene matching the name.
*
* @param name the GameObject name to search for
* @return the first GameObject found or null if not found
*/
public GameObject findByName(String name) {
return root.findChildByName(name);
}

/**
* Returns an Array of all GameObjects in the scene matching the name.
* Traversing the scene can be expensive, cache these results if you need them often.
*
* @param name the GameObject name to search for
* @return Array of all matching GameObjects
*/
public Array<GameObject> findAllByName(String name) {
return root.findChildrenByName(name);
}

/**
* Returns an Array of all GameObjects in the scene that have the given Component.Type
* Traversing the scene can be expensive, cache these results if you need them often.
*
* @param type the Component Type to search for
* @return Array of all matching GameObjects
*/
public Array<GameObject> findAllByComponent(Component.Type type) {
return root.findChildrenByComponent(type);
}

/**
* Returns an Array of all scene GameObjects that have the given Tag
* Traversing the scene can be expensive, cache these results if you need them often.
*
* @param tag the string tag to search for
* @return Array of all matching GameObjects
*/
public Array<GameObject> findAllByTag(String tag) {
return root.findChildrenByTag(tag);
}

public GameObject getRoot() {
return root;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class LightUtils {
*/
public static DirectionalLight getDirectionalLight(Environment env) {
DirectionalLightsAttribute dirLightAttribs = env.get(DirectionalLightsAttribute.class, DirectionalLightsAttribute.Type);
if (dirLightAttribs == null) return null;

Array<DirectionalLight> dirLights = dirLightAttribs.lights;
if (dirLights != null && dirLights.size > 0) {
return dirLights.first();
Expand Down
187 changes: 187 additions & 0 deletions commons/src/test/com/mbrlabs/mundus/commons/SceneGraphTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2016. See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.mbrlabs.mundus.commons;

import com.badlogic.gdx.utils.Array;
import com.mbrlabs.mundus.commons.env.MundusEnvironment;
import com.mbrlabs.mundus.commons.scene3d.GameObject;
import com.mbrlabs.mundus.commons.scene3d.InvalidComponentException;
import com.mbrlabs.mundus.commons.scene3d.Node;
import com.mbrlabs.mundus.commons.scene3d.SceneGraph;
import com.mbrlabs.mundus.commons.scene3d.SimpleNode;

import com.mbrlabs.mundus.commons.scene3d.components.Component;
import com.mbrlabs.mundus.commons.scene3d.components.ModelComponent;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;

/**
* @author Marcus Brummer
* @version 21-06-2016
*/
public class SceneGraphTest {

@Test
public void basicParenting() {
Node<SimpleNode> root = new SimpleNode<>(0);
Assert.assertNull(root.getChildren());

SimpleNode<SimpleNode> c0 = new SimpleNode<>(1);
root.addChild(c0);
Assert.assertEquals(1, root.getChildren().size);
Assert.assertSame(root.getChildren().first(), c0);
Assert.assertSame(root, c0.getParent());

SimpleNode<SimpleNode> c1 = new SimpleNode<>(2);
root.addChild(c1);
Assert.assertEquals(2, root.getChildren().size);
Assert.assertSame(root, c0.getParent());

c0.remove();
Assert.assertEquals(1, root.getChildren().size);
Assert.assertNull(c0.getParent());
}

@Test
public void gameObjectFindByName() {
SceneGraph sceneGraph = new SceneGraph(null);

// The GO to search for
String searchName = "SearchName";
GameObject searchObject = new GameObject(sceneGraph, searchName, -1);

// Parent GO
GameObject parent = new GameObject(sceneGraph, "Parent", -1);
parent.addChild(searchObject);

sceneGraph.addGameObject(parent);

GameObject result = sceneGraph.findByName(searchName);
Assert.assertNotNull(result);
Assert.assertEquals(searchName, result.name);
}

@Test
public void gameObjectsFindByName() {
SceneGraph sceneGraph = new SceneGraph(null);

// The GO's to search for
String searchName = "SearchName";
GameObject searchObject = new GameObject(sceneGraph, searchName, -1);
GameObject searchObjectTwo = new GameObject(sceneGraph, searchName, -1);

// Parent GO
GameObject parent = new GameObject(sceneGraph, "Parent", -1);
parent.addChild(searchObject);
parent.addChild(searchObjectTwo);

sceneGraph.addGameObject(parent);

Array<GameObject> result = sceneGraph.findAllByName(searchName);
Assert.assertEquals(2, result.size);
Assert.assertEquals(searchObject, result.get(0));
Assert.assertEquals(searchObjectTwo, result.get(1));
}

@Test
public void gameObjectsHierarchyFindByName() {
SceneGraph sceneGraph = new SceneGraph(null);

// The GO's to search for
String searchName = "SearchName";

int expectedResult = 0;

// Parent GO
GameObject parent = new GameObject(sceneGraph, "Parent", -1);
GameObject currentParent = parent;

// Create a hierarchy of Parent->Child GO's to search through
for (int i = 0; i < 4; i++) {
GameObject searchObject = new GameObject(sceneGraph, searchName, -1);
GameObject nonSearchObject = new GameObject(sceneGraph, "OtherObject", -1);
currentParent.addChild(searchObject);
currentParent.addChild(nonSearchObject);

// each iteration, current child becomes next parent
// variate which one is parent to complicate scenegraph more
currentParent = i % 2 == 0 ? searchObject : nonSearchObject;
expectedResult++;
}

int parentCount = 0;
// Add the top level parent multiple times just add complexity to scenegraph
for (int i = 0; i < 4; i++) {
sceneGraph.addGameObject(parent);
parentCount++;
}

Array<GameObject> result = sceneGraph.findAllByName(searchName);
Assert.assertEquals(expectedResult * parentCount, result.size);
}

@Test
public void gameObjectsFindByComponent() throws InvalidComponentException {
Scene mock = Mockito.mock(Scene.class);
mock.environment = new MundusEnvironment();

SceneGraph sceneGraph = new SceneGraph(null);
sceneGraph.scene = mock;

// The GO's to search for
String searchName = "SearchName";
GameObject searchObject = new GameObject(sceneGraph, searchName, -1);
GameObject searchObjectTwo = new GameObject(sceneGraph, searchName, -1);

searchObject.addComponent(new ModelComponent(searchObject));
searchObjectTwo.addComponent(new ModelComponent(searchObjectTwo));

// Parent GO
GameObject parent = new GameObject(sceneGraph, "Parent", -1);
parent.addChild(searchObject);
parent.addChild(searchObjectTwo);

sceneGraph.addGameObject(parent);

Array<GameObject> result = sceneGraph.findAllByComponent(Component.Type.MODEL);
Assert.assertEquals(2, result.size);
Assert.assertEquals(searchObject, result.get(0));
Assert.assertEquals(searchObjectTwo, result.get(1));
}

@Test
public void gameObjectFindByTag() {
SceneGraph sceneGraph = new SceneGraph(null);

// The GO to search for
String searchTag = "SearchTag";
GameObject searchObject = new GameObject(sceneGraph, "search", -1);
searchObject.addTag(searchTag);

// Parent GO
GameObject parent = new GameObject(sceneGraph, "Parent", -1);
parent.addChild(searchObject);

sceneGraph.addGameObject(parent);

Array<GameObject> result = sceneGraph.findAllByTag(searchTag);
Assert.assertEquals(1, result.size);
Assert.assertEquals(searchTag, result.first().getTags().first());
}

}

This file was deleted.

1 change: 1 addition & 0 deletions gdx-runtime/CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Add render event handling
- Change scene camera to be base camera class
- Add active boolean to Skybox class to toggle rendering
- Added new convenience methods to SceneGraph and GameObject for searching for GameObjects

[0.4.0] ~ 10/12/2022
- [BREAKING CHANGE] The loadScene method for the runtime has changed. A ModelBatch is no longer required to be passed in.
Expand Down

0 comments on commit 371b8ef

Please sign in to comment.