diff --git a/editor/src/com/talosvfx/talos/editor/addons/scene/logic/componentwrappers/ParticleComponentProvider.java b/editor/src/com/talosvfx/talos/editor/addons/scene/logic/componentwrappers/ParticleComponentProvider.java index 07af137d..045ff38e 100644 --- a/editor/src/com/talosvfx/talos/editor/addons/scene/logic/componentwrappers/ParticleComponentProvider.java +++ b/editor/src/com/talosvfx/talos/editor/addons/scene/logic/componentwrappers/ParticleComponentProvider.java @@ -1,6 +1,8 @@ package com.talosvfx.talos.editor.addons.scene.logic.componentwrappers; import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.utils.Array; import com.talosvfx.talos.editor.addons.scene.apps.spriteeditor.SpriteEditorApp; import com.talosvfx.talos.editor.addons.scene.assets.AssetRepository; @@ -33,6 +35,12 @@ public Array getListOfProperties () { // } PropertyWidget descriptorWidget = WidgetFactory.generateForGameAsset(component, "gameAsset", null, "Effect", GameAssetType.VFX); + descriptorWidget.addListener(new ChangeListener() { + @Override + public void changed (ChangeEvent event, Actor actor) { + component.setEffectRef(null); + } + }); ButtonPropertyWidget linkedToWidget = new ButtonPropertyWidget("Effect Project", "Edit", new ButtonPropertyWidget.ButtonListener() { @Override diff --git a/editor/src/com/talosvfx/talos/editor/project2/ProjectSplash.java b/editor/src/com/talosvfx/talos/editor/project2/ProjectSplash.java index d92ef919..14ead6d0 100644 --- a/editor/src/com/talosvfx/talos/editor/project2/ProjectSplash.java +++ b/editor/src/com/talosvfx/talos/editor/project2/ProjectSplash.java @@ -169,7 +169,12 @@ private void buildOpenTable (Table table) { if(iter++ > 5) break; FileHandle handle = Gdx.files.absolute(recentProject.getProjectPath()); - ButtonLabel recentProjectLabel = new ButtonLabel(SharedResources.skin.getDrawable("ic-file-blank"), handle.name()); + String pathEnd = handle.path(); + //Show maximum of 40 characters of path end + if(pathEnd.length() > 40) { + pathEnd = "..." + pathEnd.substring(pathEnd.length() - 40); + } + ButtonLabel recentProjectLabel = new ButtonLabel(SharedResources.skin.getDrawable("ic-file-blank"), handle.name() + " (" + pathEnd + ")"); recentProjectLabel.addListener(new ClickListener() { @Override public void clicked (InputEvent event, float x, float y) { diff --git a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/GameObjectRenderer.java b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/GameObjectRenderer.java index 05974548..01aaf6b0 100644 --- a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/GameObjectRenderer.java +++ b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/GameObjectRenderer.java @@ -11,6 +11,7 @@ import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.GdxRuntimeException; +import com.esotericsoftware.spine.Bone; import com.talosvfx.talos.runtime.scene.components.*; import com.talosvfx.talos.runtime.scene.render.*; import com.talosvfx.talos.runtime.RuntimeContext; @@ -19,338 +20,431 @@ import java.util.Comparator; public class GameObjectRenderer { - private ComponentRenderer spriteRenderer; - private ComponentRenderer mapRenderer; - private ComponentRenderer> particleRenderer; - private ComponentRenderer> routineRenderer; - private ComponentRenderer spineRenderer; - private ComponentRenderer pathRenderer; + private ComponentRenderer spriteRenderer; + private ComponentRenderer mapRenderer; + private ComponentRenderer> particleRenderer; + private ComponentRenderer> routineRenderer; + private ComponentRenderer spineRenderer; + private ComponentRenderer pathRenderer; - public final Comparator layerAndDrawOrderComparator; - public final Comparator yDownDrawOrderComparator; + public final Comparator layerAndDrawOrderComparator; + public final Comparator yDownDrawOrderComparator; - public final Comparator parentSorter; + public final Comparator parentSorter; - private Camera camera; + private Camera camera; - @Getter - private boolean skipUpdates; + @Getter + private boolean skipUpdates; - private static TextureRegion brokenRegion; + private static TextureRegion brokenRegion; - public GameObjectRenderer () { - brokenRegion = new TextureRegion(new Texture(Gdx.files.classpath("missing.png"))); + public GameObjectRenderer () { + brokenRegion = new TextureRegion(new Texture(Gdx.files.classpath("missing.png"))); - spriteRenderer = createSpriteRenderer(); - mapRenderer = createMapRenderer(); - particleRenderer = createParticleRenderer(); - routineRenderer = createRoutineRenderer(); - spineRenderer = createSpineRenderer(); - pathRenderer = createPathRenderer(); + spriteRenderer = createSpriteRenderer(); + mapRenderer = createMapRenderer(); + particleRenderer = createParticleRenderer(); + routineRenderer = createRoutineRenderer(); + spineRenderer = createSpineRenderer(); + pathRenderer = createPathRenderer(); - layerAndDrawOrderComparator = new Comparator() { - @Override - public int compare (GameObject o1, GameObject o2) { - float aSort = GameObjectRenderer.getDrawOrderSafe(o1); - float bSort = GameObjectRenderer.getDrawOrderSafe(o2); - return Float.compare(aSort, bSort); - } - }; + layerAndDrawOrderComparator = new Comparator() { + @Override + public int compare (GameObject o1, GameObject o2) { + float aSort = GameObjectRenderer.getDrawOrderSafe(o1); + float bSort = GameObjectRenderer.getDrawOrderSafe(o2); + return Float.compare(aSort, bSort); + } + }; - yDownDrawOrderComparator = new Comparator() { - @Override - public int compare (GameObject o1, GameObject o2) { - float aSort = GameObjectRenderer.getBottomY(o1); - float bSort = GameObjectRenderer.getBottomY(o2); - return -Float.compare(aSort, bSort); - } - }; + yDownDrawOrderComparator = new Comparator() { + @Override + public int compare (GameObject o1, GameObject o2) { + float aSort = GameObjectRenderer.getBottomY(o1); + float bSort = GameObjectRenderer.getBottomY(o2); + return -Float.compare(aSort, bSort); + } + }; - parentSorter = new Comparator() { - @Override - public int compare (GameObject o1, GameObject o2) { - SceneLayer o1Layer = GameObjectRenderer.getLayerSafe(o1); - SceneLayer o2Layer = GameObjectRenderer.getLayerSafe(o2); + parentSorter = new Comparator() { + @Override + public int compare (GameObject o1, GameObject o2) { + SceneLayer o1Layer = GameObjectRenderer.getLayerSafe(o1); + SceneLayer o2Layer = GameObjectRenderer.getLayerSafe(o2); - if (o1Layer.equals(o2Layer)) { + if (o1Layer.equals(o2Layer)) { - RenderStrategy renderStrategy = o1Layer.getRenderStrategy(); + RenderStrategy renderStrategy = o1Layer.getRenderStrategy(); - Comparator sorter = getSorter(renderStrategy); + Comparator sorter = getSorter(renderStrategy); - return sorter.compare(o1, o2); - } else { - return Integer.compare(o1Layer.getIndex(), o2Layer.getIndex()); - } - } - }; + return sorter.compare(o1, o2); + } else { + return Integer.compare(o1Layer.getIndex(), o2Layer.getIndex()); + } + } + }; - } + } - private Comparator getSorter (RenderStrategy renderMode) { - switch (renderMode) { - case SCENE: - return layerAndDrawOrderComparator; - case YDOWN: - return yDownDrawOrderComparator; - } - - throw new GdxRuntimeException("No sorter found for render mode: " + renderMode); - } - - private static float getBottomY (GameObject gameObject) { - if (gameObject.hasComponentType(RendererComponent.class)) { - RendererComponent componentAssignableFrom = gameObject.getComponentAssignableFrom(RendererComponent.class); - TransformComponent transformComponent = gameObject.getComponent(TransformComponent.class); - - float y = transformComponent.worldPosition.y; - - - if (componentAssignableFrom instanceof SpriteRendererComponent) { - Vector2 size = ((SpriteRendererComponent)componentAssignableFrom).size; - Vector2 worldScale = transformComponent.worldScale; - - float totalHeight = size.y * worldScale.y; - y -= totalHeight/2f; - } - - if (componentAssignableFrom instanceof RendererComponent) { - float fakeOffsetY = componentAssignableFrom.fakeOffsetY; - y += fakeOffsetY; - } - - return y; - - } else { - if (gameObject.hasComponent(TransformComponent.class)) { - TransformComponent component = gameObject.getComponent(TransformComponent.class); - return component.worldPosition.y; - } - } - - return 0; - } - - private static SceneLayer getLayerSafe(GameObject gameObject) { - if (gameObject.hasComponentType(RendererComponent.class)) { - RendererComponent rendererComponent = gameObject.getComponentAssignableFrom(RendererComponent.class); - return rendererComponent.sortingLayer; - } - - return RuntimeContext.getInstance().sceneData.getPreferredSceneLayer(); - } - - public static float getDrawOrderSafe (GameObject gameObject) { - if (gameObject.hasComponentType(RendererComponent.class)) { - RendererComponent rendererComponent = gameObject.getComponentAssignableFrom(RendererComponent.class); - return rendererComponent.orderingInLayer; - } - - return -55; - } - - - protected ComponentRenderer createMapRenderer () { - return new MapComponentRenderer(this); - } - - protected ComponentRenderer> createRoutineRenderer () { - return new RoutineComponentRenderer(this); - } - - protected ComponentRenderer createSpineRenderer () { - return new SkeletonComponentRenderer(this); - } - - protected ComponentRenderer> createParticleRenderer () { - return new SimpleParticleComponentRenderer(this); - } - - protected ComponentRenderer createSpriteRenderer () { - return new SpriteComponentRenderer(this); - } - - protected ComponentRenderer createPathRenderer () { - return new PathComponentRenderer(this); - } - - protected void sort (Array list) { - list.sort(parentSorter); - } - - public void update (GameObject gameObject, float delta) { - if (!gameObject.active || !gameObject.isEditorVisible()) - return; - if (gameObject.hasComponent(TransformComponent.class) && !gameObject.hasComponent(BoneComponent.class)) { - TransformComponent transform = gameObject.getComponent(TransformComponent.class); - - transform.worldPosition.set(transform.position); - transform.worldScale.set(transform.scale); - transform.worldRotation = transform.rotation; - - if (gameObject.parent != null) { - - if (gameObject.parent.hasComponent(TransformComponent.class)) { - //Combine our world with the parent - - TransformComponent parentTransform = gameObject.parent.getComponent(TransformComponent.class); - transform.worldPosition.scl(parentTransform.worldScale); - transform.worldPosition.rotateDeg(parentTransform.worldRotation); - transform.worldPosition.add(parentTransform.worldPosition); - - transform.worldRotation += parentTransform.worldRotation; - transform.worldScale.scl(parentTransform.worldScale); - } - } - } - - // if root has render component try mixing colors if they exist - if (gameObject.hasComponentType(RendererComponent.class)) { - final RendererComponent rendererComponent = gameObject.getComponentAssignableFrom(RendererComponent.class); - - // check if render component has color value - if (rendererComponent instanceof IColorHolder) { - final IColorHolder colorHolder = (IColorHolder)rendererComponent; - - // update final color by Renderer color - final Color finalColor = (colorHolder.getFinalColor()); - finalColor.set(colorHolder.getColor()); - - // should inherit parent color update final color by parent color - if (colorHolder.shouldInheritParentColor()) { - if (gameObject.parent != null) { - // check if parent contains render component - if (gameObject.parent.hasComponentType(RendererComponent.class)) { - final RendererComponent parentRendererComponent = gameObject.parent.getComponentAssignableFrom(RendererComponent.class); - - // check if parent render component has color value - if (parentRendererComponent instanceof IColorHolder) { - // combine colors - finalColor.mul(((IColorHolder)parentRendererComponent).getFinalColor()); - } - } - } - } - } - } - - if (gameObject.getGameObjects() != null) { - for (int i = 0; i < gameObject.getGameObjects().size; i++) { - GameObject child = gameObject.getGameObjects().get(i); - update(child, delta); - } - } - } - - protected void fillRenderableEntities (Array rootObjects, Array list) { - for (GameObject root : rootObjects) { - if (!root.active || !root.isEditorVisible()) continue; - - boolean childrenVisibleFlag = true; - if (root.hasComponentType(RendererComponent.class)) { - RendererComponent rendererComponent = root.getComponentAssignableFrom(RendererComponent.class); - childrenVisibleFlag = rendererComponent.childrenVisible; - if (rendererComponent.visible) { - list.add(root); - } - } - if (childrenVisibleFlag) { - if (root.getGameObjects() != null) { - fillRenderableEntities(root.getGameObjects(), list); - } - } - } - - } - - - public static void renderBrokenComponent (Batch batch, GameObject gameObject, TransformComponent transformComponent) { - - float width = 1f; - float height = 1f; - if (gameObject.hasComponent(SpriteRendererComponent.class)) { - SpriteRendererComponent component = gameObject.getComponent(SpriteRendererComponent.class); - width = component.size.x; - height = component.size.y; - } - - batch.draw(brokenRegion, - transformComponent.worldPosition.x - 0.5f, transformComponent.worldPosition.y - 0.5f, - 0.5f, 0.5f, - 1f, 1f, - width * transformComponent.worldScale.x, height * transformComponent.worldScale.y, - transformComponent.worldRotation); - } - - - Array temp = new Array<>(); - - - public void buildRenderState (PolygonBatch batch, RenderState state, Array rootObjects) { - boolean hiearchyDirty = false; - for (GameObject rootObject : rootObjects) { - if (rootObject.hierarchyDirty) { - hiearchyDirty = true; - - //reset it because we gonna sort it this pass - rootObject.hierarchyDirty = false; - } - } - if (hiearchyDirty) { - state.list.clear(); - fillRenderableEntities(rootObjects, state.list); - } - sort(state.list); - } - - - public void renderObject (Batch batch, GameObject gameObject) { - if (gameObject.hasComponent(RoutineRendererComponent.class)) { - routineRenderer.render(batch, camera, gameObject, gameObject.getComponent(RoutineRendererComponent.class)); - } - - if (gameObject.hasComponent(SpriteRendererComponent.class)) { - spriteRenderer.render(batch, camera, gameObject, gameObject.getComponent(SpriteRendererComponent.class)); - } else if (gameObject.hasComponent(ParticleComponent.class)) { - particleRenderer.render(batch,camera, gameObject, gameObject.getComponent(ParticleComponent.class)); - } else if (gameObject.hasComponent(SpineRendererComponent.class)) { - spineRenderer.render(batch, camera, gameObject, gameObject.getComponent(SpineRendererComponent.class)); - } else if (gameObject.hasComponent(MapComponent.class)) { - mapRenderer.render(batch, camera, gameObject, gameObject.getComponent(MapComponent.class)); - } else if (gameObject.hasComponent(PathRendererComponent.class)) { - pathRenderer.render(batch, camera, gameObject, gameObject.getComponent(PathRendererComponent.class)); - } - - } - - public void buildRenderStateAndRender (PolygonBatch batch, Camera camera, RenderState state, GameObject root) { - temp.clear(); - temp.add(root); - buildRenderStateAndRender(batch, camera, state, temp); - } - - public void buildRenderStateAndRender (PolygonBatch batch, Camera camera, RenderState state, Array rootObjects) { - setCamera(camera); - - buildRenderState(batch, state, rootObjects); - for (int i = 0; i < state.list.size; i++) { - GameObject gameObject = state.list.get(i); - renderObject(batch, gameObject); - } - batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); - } - - public void setCamera (Camera camera) { - this.camera = camera; - } - - /** - * Any renderers that may want to skip updates do it here - * @param skipUpdates - */ - public void setSkipUpdates (boolean skipUpdates) { - this.skipUpdates = skipUpdates; - } + private Comparator getSorter (RenderStrategy renderMode) { + switch (renderMode) { + case SCENE: + return layerAndDrawOrderComparator; + case YDOWN: + return yDownDrawOrderComparator; + } + + throw new GdxRuntimeException("No sorter found for render mode: " + renderMode); + } + + private static float getBottomY (GameObject gameObject) { + if (gameObject.hasComponentType(RendererComponent.class)) { + RendererComponent componentAssignableFrom = gameObject.getComponentAssignableFrom(RendererComponent.class); + TransformComponent transformComponent = gameObject.getComponent(TransformComponent.class); + + float y = transformComponent.worldPosition.y; + + + if (componentAssignableFrom instanceof SpriteRendererComponent) { + Vector2 size = ((SpriteRendererComponent) componentAssignableFrom).size; + Vector2 worldScale = transformComponent.worldScale; + + float totalHeight = size.y * worldScale.y; + y -= totalHeight / 2f; + } + + if (componentAssignableFrom instanceof RendererComponent) { + float fakeOffsetY = componentAssignableFrom.fakeOffsetY; + y += fakeOffsetY; + } + + return y; + + } else { + if (gameObject.hasComponent(TransformComponent.class)) { + TransformComponent component = gameObject.getComponent(TransformComponent.class); + return component.worldPosition.y; + } + } + + return 0; + } + + private static SceneLayer getLayerSafe (GameObject gameObject) { + if (gameObject.hasComponentType(RendererComponent.class)) { + RendererComponent rendererComponent = gameObject.getComponentAssignableFrom(RendererComponent.class); + return rendererComponent.sortingLayer; + } + + return RuntimeContext.getInstance().sceneData.getPreferredSceneLayer(); + } + + public static float getDrawOrderSafe (GameObject gameObject) { + if (gameObject.hasComponentType(RendererComponent.class)) { + RendererComponent rendererComponent = gameObject.getComponentAssignableFrom(RendererComponent.class); + return rendererComponent.orderingInLayer; + } + + return -55; + } + + + protected ComponentRenderer createMapRenderer () { + return new MapComponentRenderer(this); + } + + protected ComponentRenderer> createRoutineRenderer () { + return new RoutineComponentRenderer(this); + } + + protected ComponentRenderer createSpineRenderer () { + return new SkeletonComponentRenderer(this); + } + + protected ComponentRenderer> createParticleRenderer () { + return new SimpleParticleComponentRenderer(this); + } + + protected ComponentRenderer createSpriteRenderer () { + return new SpriteComponentRenderer(this); + } + + protected ComponentRenderer createPathRenderer () { + return new PathComponentRenderer(this); + } + + protected void sort (Array list) { + list.sort(parentSorter); + } + + public static void debugTransforms (GameObject gameObject, int indent) { + if (gameObject.hasComponent(TransformComponent.class)) { + TransformComponent transformComponent = gameObject.getComponent(TransformComponent.class); + StringBuilder builder = new StringBuilder(); + + //Indent all by indent * 2 spaces + //print in format + //name + // local: [Position]: (x,y,z), [Scale]: (x,y,z), [Rotation]: (x,y,z) + // world: [Position]: (x,y,z), [Scale]: (x,y,z), [Rotation]: (x,y,z) + + for (int i = 0; i < indent; i++) { + builder.append(" "); + } + builder.append(gameObject.getName()); + builder.append("\n"); + for (int i = 0; i < indent; i++) { + builder.append(" "); + } + builder.append(" local: [Position]: "); + builder.append(transformComponent.position); + builder.append(", [Scale]: "); + builder.append(transformComponent.scale); + builder.append(", [Rotation]: "); + builder.append(transformComponent.rotation); + builder.append("\n"); + + for (int i = 0; i < indent; i++) { + builder.append(" "); + } + builder.append(" world: [Position]: "); + builder.append(transformComponent.worldPosition); + builder.append(", [Scale]: "); + builder.append(transformComponent.worldScale); + builder.append(", [Rotation]: "); + builder.append(transformComponent.worldRotation); + builder.append("\n"); + + if (gameObject.hasComponent(ParticleComponent.class)) { + ParticleComponent particleComponent = gameObject.getComponent(ParticleComponent.class); + if (particleComponent.getEffectRef() != null) { + float worldRotation = particleComponent.getEffectRef().getWorldRotation(); + Vector2 worldScale = particleComponent.getEffectRef().getWorldScale(); + //print it + for (int i = 0; i < indent; i++) { + builder.append(" "); + } + builder.append(" particle: [Position]: "); + builder.append(particleComponent.getEffectRef().getPosition()); + builder.append(", [Scale]: "); + builder.append(worldScale); + builder.append(", [Rotation]: "); + builder.append(worldRotation); + + + } + + } + System.out.println(builder); + + } + for (GameObject object : gameObject.getGameObjects()) { + debugTransforms(object, indent + 1); + } + } + + public void update (GameObject gameObject, float delta) { + if (!gameObject.active || !gameObject.isEditorVisible()) + return; + + //If we are a skeleton, we update it now + if (gameObject.hasComponent(SpineRendererComponent.class)) { + SpineRendererComponent spineRendererComponent = gameObject.getComponent(SpineRendererComponent.class); + spineRenderer.update(gameObject, spineRendererComponent, delta); + + // update bone game objects + if (spineRendererComponent.generateGameObjectBones) { + Array boneGOs = gameObject.getChildrenWithBoneComponent(); + + for (GameObject boneGO : boneGOs) { + BoneComponent boneComponent = boneGO.getComponent(BoneComponent.class); + Bone bone = boneComponent.getBone(); + TransformComponent transform = boneGO.getComponent(TransformComponent.class); + + transform.worldScale.set(bone.getWorldScaleX(), bone.getWorldScaleY()); + transform.worldRotation = bone.localToWorldRotation(bone.getRotation()); + transform.worldPosition.set(bone.getWorldX(), bone.getWorldY()); + + transform.position.set(bone.getX(), bone.getY()); + transform.rotation = bone.getRotation(); + transform.scale.set(bone.getScaleX(), bone.getScaleY()); + } + } + } + + if (gameObject.hasComponent(TransformComponent.class) && !gameObject.hasComponent(BoneComponent.class)) { + TransformComponent transform = gameObject.getComponent(TransformComponent.class); + + transform.worldPosition.set(transform.position); + transform.worldScale.set(transform.scale); + transform.worldRotation = transform.rotation; + + if (gameObject.parent != null) { + + if (gameObject.parent.hasComponent(TransformComponent.class)) { + //Combine our world with the parent + + TransformComponent parentTransform = gameObject.parent.getComponent(TransformComponent.class); + transform.worldPosition.scl(parentTransform.worldScale); + transform.worldPosition.rotateDeg(parentTransform.worldRotation); + transform.worldPosition.add(parentTransform.worldPosition); + + transform.worldRotation += parentTransform.worldRotation; + transform.worldScale.scl(parentTransform.worldScale); + } + } + } + + // if root has render component try mixing colors if they exist + if (gameObject.hasComponentType(RendererComponent.class)) { + final RendererComponent rendererComponent = gameObject.getComponentAssignableFrom(RendererComponent.class); + + // check if render component has color value + if (rendererComponent instanceof IColorHolder) { + final IColorHolder colorHolder = (IColorHolder) rendererComponent; + + // update final color by Renderer color + final Color finalColor = (colorHolder.getFinalColor()); + finalColor.set(colorHolder.getColor()); + + // should inherit parent color update final color by parent color + if (colorHolder.shouldInheritParentColor()) { + if (gameObject.parent != null) { + // check if parent contains render component + if (gameObject.parent.hasComponentType(RendererComponent.class)) { + final RendererComponent parentRendererComponent = gameObject.parent.getComponentAssignableFrom(RendererComponent.class); + + // check if parent render component has color value + if (parentRendererComponent instanceof IColorHolder) { + // combine colors + finalColor.mul(((IColorHolder) parentRendererComponent).getFinalColor()); + } + } + } + } + } + } + + if (gameObject.getGameObjects() != null) { + for (int i = 0; i < gameObject.getGameObjects().size; i++) { + GameObject child = gameObject.getGameObjects().get(i); + update(child, delta); + } + } + } + + protected void fillRenderableEntities (Array rootObjects, Array list) { + for (GameObject root : rootObjects) { + if (!root.active || !root.isEditorVisible()) continue; + + boolean childrenVisibleFlag = true; + if (root.hasComponentType(RendererComponent.class)) { + RendererComponent rendererComponent = root.getComponentAssignableFrom(RendererComponent.class); + childrenVisibleFlag = rendererComponent.childrenVisible; + if (rendererComponent.visible) { + list.add(root); + } + } + if (childrenVisibleFlag) { + if (root.getGameObjects() != null) { + fillRenderableEntities(root.getGameObjects(), list); + } + } + } + + } + + + public static void renderBrokenComponent (Batch batch, GameObject gameObject, TransformComponent transformComponent) { + + float width = 1f; + float height = 1f; + if (gameObject.hasComponent(SpriteRendererComponent.class)) { + SpriteRendererComponent component = gameObject.getComponent(SpriteRendererComponent.class); + width = component.size.x; + height = component.size.y; + } + + batch.draw(brokenRegion, + transformComponent.worldPosition.x - 0.5f, transformComponent.worldPosition.y - 0.5f, + 0.5f, 0.5f, + 1f, 1f, + width * transformComponent.worldScale.x, height * transformComponent.worldScale.y, + transformComponent.worldRotation); + } + + + Array temp = new Array<>(); + + + public void buildRenderState (PolygonBatch batch, RenderState state, Array rootObjects) { + boolean hiearchyDirty = false; + for (GameObject rootObject : rootObjects) { + if (rootObject.hierarchyDirty) { + hiearchyDirty = true; + + //reset it because we gonna sort it this pass + rootObject.hierarchyDirty = false; + } + } + if (hiearchyDirty) { + state.list.clear(); + fillRenderableEntities(rootObjects, state.list); + } + sort(state.list); + } + + + public void renderObject (Batch batch, GameObject gameObject) { + if (gameObject.hasComponent(RoutineRendererComponent.class)) { + routineRenderer.render(batch, camera, gameObject, gameObject.getComponent(RoutineRendererComponent.class)); + } + + if (gameObject.hasComponent(SpriteRendererComponent.class)) { + spriteRenderer.render(batch, camera, gameObject, gameObject.getComponent(SpriteRendererComponent.class)); + } else if (gameObject.hasComponent(ParticleComponent.class)) { + particleRenderer.render(batch, camera, gameObject, gameObject.getComponent(ParticleComponent.class)); + } else if (gameObject.hasComponent(SpineRendererComponent.class)) { + spineRenderer.render(batch, camera, gameObject, gameObject.getComponent(SpineRendererComponent.class)); + } else if (gameObject.hasComponent(MapComponent.class)) { + mapRenderer.render(batch, camera, gameObject, gameObject.getComponent(MapComponent.class)); + } else if (gameObject.hasComponent(PathRendererComponent.class)) { + pathRenderer.render(batch, camera, gameObject, gameObject.getComponent(PathRendererComponent.class)); + } + + } + + public void buildRenderStateAndRender (PolygonBatch batch, Camera camera, RenderState state, GameObject root) { + temp.clear(); + temp.add(root); + buildRenderStateAndRender(batch, camera, state, temp); + } + + public void buildRenderStateAndRender (PolygonBatch batch, Camera camera, RenderState state, Array rootObjects) { + setCamera(camera); + + buildRenderState(batch, state, rootObjects); + for (int i = 0; i < state.list.size; i++) { + GameObject gameObject = state.list.get(i); + renderObject(batch, gameObject); + } + batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); + } + + public void setCamera (Camera camera) { + this.camera = camera; + } + + /** + * Any renderers that may want to skip updates do it here + * + * @param skipUpdates + */ + public void setSkipUpdates (boolean skipUpdates) { + this.skipUpdates = skipUpdates; + } } diff --git a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/ComponentRenderer.java b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/ComponentRenderer.java index 681282f5..62a2b318 100644 --- a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/ComponentRenderer.java +++ b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/ComponentRenderer.java @@ -15,4 +15,8 @@ public ComponentRenderer (GameObjectRenderer gameObjectRenderer) { } public abstract void render (Batch batch, Camera camera, GameObject parent, T rendererComponent); + + public void update (GameObject gameObject, T component, float delta) { + + } } diff --git a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/SkeletonComponentRenderer.java b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/SkeletonComponentRenderer.java index b553efcc..2bf606a6 100644 --- a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/SkeletonComponentRenderer.java +++ b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/scene/render/SkeletonComponentRenderer.java @@ -26,15 +26,8 @@ public SkeletonComponentRenderer (GameObjectRenderer gameObjectRenderer) { } @Override - public void render (Batch batch, Camera camera, GameObject gameObject, SpineRendererComponent rendererComponent) { + public void update (GameObject gameObject, SpineRendererComponent spineRendererComponent, float delta) { TransformComponent parentTransform = gameObject.getComponent(TransformComponent.class); - SpineRendererComponent spineRendererComponent = gameObject.getComponent(SpineRendererComponent.class); - - GameAsset gameResource = rendererComponent.getGameResource(); - if (gameResource.isBroken()) { - GameObjectRenderer.renderBrokenComponent(batch, gameObject, parentTransform); - return; - } spineRendererComponent.skeleton.setPosition(parentTransform.worldPosition.x, parentTransform.worldPosition.y); spineRendererComponent.skeleton.setScale(parentTransform.worldScale.x * spineRendererComponent.scale, parentTransform.worldScale.y * spineRendererComponent.scale); @@ -44,7 +37,21 @@ public void render (Batch batch, Camera camera, GameObject gameObject, SpineRend spineRendererComponent.animationState.update(Gdx.graphics.getDeltaTime()); spineRendererComponent.animationState.apply(spineRendererComponent.skeleton); } + spineRendererComponent.skeleton.updateWorldTransform(); + } + + @Override + public void render (Batch batch, Camera camera, GameObject gameObject, SpineRendererComponent rendererComponent) { + TransformComponent parentTransform = gameObject.getComponent(TransformComponent.class); + SpineRendererComponent spineRendererComponent = gameObject.getComponent(SpineRendererComponent.class); + + GameAsset gameResource = rendererComponent.getGameResource(); + if (gameResource.isBroken()) { + GameObjectRenderer.renderBrokenComponent(batch, gameObject, parentTransform); + return; + } + spineRendererComponent.skeleton.getColor().set(spineRendererComponent.finalColor); skeletonRenderer.draw(batch, spineRendererComponent.skeleton); @@ -52,24 +59,7 @@ public void render (Batch batch, Camera camera, GameObject gameObject, SpineRend batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); - // update bone game objects - if (spineRendererComponent.generateGameObjectBones) { - Array boneGOs = gameObject.getChildrenWithBoneComponent(); - for (GameObject boneGO : boneGOs) { - BoneComponent boneComponent = boneGO.getComponent(BoneComponent.class); - Bone bone = boneComponent.getBone(); - TransformComponent transform = boneGO.getComponent(TransformComponent.class); - - transform.worldScale.set(bone.getWorldScaleX(), bone.getWorldScaleY()); - transform.worldRotation = bone.localToWorldRotation(bone.getRotation()); - transform.worldPosition.set(bone.getWorldX(), bone.getWorldY()); - - transform.position.set(bone.getX(), bone.getY()); - transform.rotation = bone.getRotation(); - transform.scale.set(bone.getScaleX(), bone.getScaleY()); - } - } } } diff --git a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/ParticleEffectInstance.java b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/ParticleEffectInstance.java index 6c720124..e383a23d 100644 --- a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/ParticleEffectInstance.java +++ b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/ParticleEffectInstance.java @@ -20,6 +20,7 @@ import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; import com.talosvfx.talos.runtime.vfx.render.ParticleRenderer; +import lombok.Getter; import java.util.Comparator; @@ -31,7 +32,10 @@ public class ParticleEffectInstance { Vector3 position = new Vector3(); + @Getter float worldRotation; + + @Getter Vector2 worldScale = new Vector2(1f, 1f); ScopePayload scopePayload = new ScopePayload(); diff --git a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/QuadMeshGeneratorModule.java b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/QuadMeshGeneratorModule.java index f468e5a3..80591739 100644 --- a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/QuadMeshGeneratorModule.java +++ b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/QuadMeshGeneratorModule.java @@ -2,6 +2,7 @@ import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; @@ -100,16 +101,40 @@ public void render (ParticleRenderer particleRenderer, MaterialModule materialMo float V = 0f; float V2 = 1f; + float offsetXNormalized = 0f; + float offsetYNormalized = 0f; + + float atlasOffsetScaleX = 1f; + float atlasOffsetScaleY = 1f; if (materialModule instanceof SpriteMaterialModule) { - TextureRegion textureRegion = ((SpriteMaterialModule)materialModule).getTextureRegion(); - if (textureRegion == null) return; + TextureAtlas.AtlasSprite atlasSprite = ((SpriteMaterialModule)materialModule).getTextureRegion(); + if (atlasSprite == null) return; + + TextureAtlas.AtlasRegion atlasRegion = atlasSprite.getAtlasRegion(); + + float offsetX = atlasRegion.getRegionX(); + float offsetY = atlasRegion.offsetY; + + int regionWidth = atlasRegion.getRegionWidth(); + int regionHeight = atlasRegion.getRegionHeight(); + + int originalWidth = atlasRegion.originalWidth; + int originalHeight = atlasRegion.originalHeight; - U = textureRegion.getU(); - U2 = textureRegion.getU2(); - V = textureRegion.getV(); - V2 = textureRegion.getV2(); + atlasOffsetScaleX = originalWidth / (float) regionWidth; + atlasOffsetScaleY = originalHeight / (float) regionHeight; + + // Calculate the normalized offset + offsetXNormalized = offsetX / (float) originalWidth; + offsetYNormalized = offsetY / (float) originalHeight; + + + U = atlasSprite.getU(); + U2 = atlasSprite.getU2(); + V = atlasSprite.getV(); + V2 = atlasSprite.getV2(); } rightWorldSpace.set(viewValues[0], viewValues[4], viewValues[8]); @@ -160,6 +185,9 @@ public void render (ParticleRenderer particleRenderer, MaterialModule materialMo float halfWidth = width / 2f; float halfHeight = height / 2f; + halfWidth /= atlasOffsetScaleX; + halfHeight /= atlasOffsetScaleY; + p1.set(-halfWidth, -halfHeight, 0); p2.set(halfWidth, -halfHeight, 0); p3.set(halfWidth, halfHeight, 0); @@ -243,6 +271,17 @@ public void render (ParticleRenderer particleRenderer, MaterialModule materialMo //get uvs from material +// p1.x = (p1.x + offsetXNormalized) * atlasOffsetScaleX; +// p1.y = (p1.y + offsetYNormalized) * atlasOffsetScaleY; +// +// p2.x = (p2.x + offsetXNormalized) * atlasOffsetScaleX; +// p2.y = (p2.y + offsetYNormalized) * atlasOffsetScaleY; +// +// p3.x = (p3.x + offsetXNormalized) * atlasOffsetScaleX; +// p3.y = (p3.y + offsetYNormalized) * atlasOffsetScaleY; +// +// p4.x = (p4.x + offsetXNormalized) * atlasOffsetScaleX; +// p4.y = (p4.y + offsetYNormalized) * atlasOffsetScaleY; verts[idx++] = p1.x; // x1 verts[idx++] = p1.y; // y1 diff --git a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/SpriteMaterialModule.java b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/SpriteMaterialModule.java index 0bcb641f..6b3fa0c7 100644 --- a/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/SpriteMaterialModule.java +++ b/runtimes/talos/src/main/java/com/talosvfx/talos/runtime/vfx/modules/SpriteMaterialModule.java @@ -29,14 +29,13 @@ import com.talosvfx.talos.runtime.assets.GameAsset; import com.talosvfx.talos.runtime.assets.GameAssetType; import com.talosvfx.talos.runtime.assets.GameResourceOwner; -import com.talosvfx.talos.runtime.vfx.ParticleEmitterDescriptor; import com.talosvfx.talos.runtime.vfx.values.ModuleValue; public class SpriteMaterialModule extends MaterialModule implements GameResourceOwner, GameAsset.GameAssetUpdateListener { - private transient TextureRegion region; + private transient TextureAtlas.AtlasSprite region; public GameAsset asset; @@ -86,7 +85,7 @@ public void setToDefault () { @Override public void onUpdate () { if (asset != null && !asset.isBroken()) { - region = new TextureRegion(asset.getResource()); + region = new TextureAtlas.AtlasSprite(asset.getResource()); } } @@ -119,13 +118,13 @@ public void setGameAsset (GameAsset gameAsset) { asset.listeners.add(this); if (asset != null && !asset.isBroken()) { - region = new TextureRegion(asset.getResource()); + region = new TextureAtlas.AtlasSprite(asset.getResource()); } else { System.out.println("Sprite material asset broken " + asset.nameIdentifier); } } - public TextureRegion getTextureRegion () { + public TextureAtlas.AtlasSprite getTextureRegion () { return region; }