Filling a custom-shaped Clutter Actor with a Cairo-drawn canvas - cairo

Clutter 1.12
Cogl 1.10
Vala or C or Python.
I might have a fundamental misunderstanding here —
I think of "Actors" as 3D polygon things. I think of their colours as either vertex colors or as texture-mapping. In this light, I have been trying to draw a custom Actor and fill it with stuff drawn via Cairo. I'm not getting anywhere.
Code is included below (in Vala). Can anyone set me right about Clutter's basics (the docs just don't cut it) or, if I'm close, help me get that code working.
I expect to see a rounded rectangle with a smiley face within. What I'm seeing instead is the Cogl path fill covering* the face. I think the paint() is being done after the drawme()
*If you set the Clutter.Color to "#0001" in method paint(), you'll see this.
/*
Clutter Actor with custom paint() and filled with a Cairo-drawn texture.
(NOT yet working.)
Compile with:
valac \
--pkg clutter-1.0 \
--pkg cogl-1.0 \
somename.vala
*/
public class SmileyRect : Clutter.Actor {
//private vars
private Clutter.Canvas _canvas;
private bool _flip = false;
private int _w = 300;
private int _h = 300;
//Constructor
construct {
_canvas = new Clutter.Canvas();
_canvas.set_size( this._w, this._h );
this.set_size( this._w, this._h );
this.set_content( _canvas );
//Connect to the draw signal - this is as-per Clutter docs.
_canvas.draw.connect( drawme );
//Make it reactive and connect to the button-press-event
this.set_reactive( true );
this.button_press_event.connect( button_press );
}
/*
Button press signal handler.
Changes the colour of what will be painted on the canvas.
*/
private bool button_press ( Clutter.ButtonEvent evt ) {
this._flip = !this._flip; //Jiggle a value.
this.redraw(); // Forces re-run of the drawme() method.
return true; //all done with this signal.
}
//Common function to draw Cogl stuff - used in paint and pick.
private void draw_rr( Clutter.Color? color ) {
if (color != null ) { Cogl.set_source_color4ub(color.red,color.green,color.blue,color.alpha); }
Cogl.Path.round_rectangle( 0, 0, this._w, this._h, 15, 0.3f );
Cogl.Path.close();
// When from paint():
// Is there some way to fill this Cogl path with the contents
// of this._canvas? Or some other paradigm?
if (color != null ) { Cogl.Path.fill(); }
}
/* Some kind of freaky, this magic paint() thing.
Took me ages to get it running.
I want to draw a rounded rectangle as the basic shape
of this actor.
*/
public override void paint() {
stdout.printf("paint runs.\n");
// I did try a transparent color #0000 - just to see. No go.
// #000f - draws a black rounded rect *OVER* the Cairo canvas. It covers
// the smiley face.
this.draw_rr( Clutter.Color.from_string("#0000") );
}
/* I followed paint() example, but the argument was tricky.
I eventually found it from some PyClutter source code.
*/
public override void pick(Clutter.Color color) {
stdout.printf("pick runs.\n");
this.draw_rr( color );
}
/*
Draws the Cairo art to the canvas.
I want this art to be the bitmap/pixmap that *fills* the
basic rounded rectangle shape of this actor.
i.e I want the smile face cairo rectangle to be *within* the
polygon that is draw via Cogl in paint() method.
Does this even make sense?
*/
private bool drawme( Cairo.Context ctx, int width, int height) {
//Draw a rectangle
double[] col;
if (this._flip) {
col = {1f,0f,0f};
}
else {
col = {0f,1f,0f};
}
ctx.set_source_rgb( col[0], col[1], col[2] );
ctx.rectangle( 0, 0, width, height );
ctx.fill();
//Draw a smiley face.
// Aside: Been trying to avoid all the weird casting of the floats/doubles used below
// (see the generated c source.) Not at all sure what's going on there.
// Also not sure about that 'f' on the numbers. Where/when do I have to use it?
double pi = 3.14f;
double w = (double) width;
double h = (double) height;
ctx.set_line_width( 6f );
ctx.set_source_rgb( 0f, 0f, 0.8f );
//eyes
ctx.move_to( w/3f, h/3f );
ctx.rel_line_to( 0f, h/6f );
ctx.move_to( 2*(w/3f), h/3f );
ctx.rel_line_to( 0f, h/6f );
ctx.stroke();
ctx.set_source_rgb( 1f, 1f, 0f );
double rad = (w > h) ? h : w;
//face
ctx.arc( w/2f, h/2f, (rad/2f) - 20f,0f,2f * pi );
ctx.stroke();
//smile
ctx.arc( w/2f, h/2f, (rad/3f) -10f, pi/3f, 2f * (pi/3f) );
ctx.stroke();
return true;
}
//Redraw - forces invalidate which trips the draw event
public void redraw() {
this._canvas.invalidate();
}
} //end SmileyRect class
void main( string[] args ) {
Clutter.init(ref args);
var stage = new Clutter.Stage();
stage.set_size(400,400);
var rs = new SmileyRect();
stage.add_child(rs);
rs.redraw();
stage.destroy.connect(Clutter.main_quit);
stage.show();
Clutter.main();
}

Related

Tween color overlay of Spine animation

Im using Unity3D and character animations imported from Spine. I want to add a color overlay over those characters. For example, I have one character, I want to make it a "shadow" character, so I add the black color over it in this way:
GetComponent<SkeletonAnimation>().Skeleton.color = new Color(0f,0f,0f);
Nevertheless, I want a Tween between the regular color, and the new color. But, unfortunately, I can't do it with the DOColor method of DOTween. I try
GetComponent<SkeletonAnimation>().Skeleton.DOColor(Color.Black,1);
But the method DOColor for Skeleton doesn't exists. So which is the way to follow to accomplish this?
DoColor, DoMove, etc are shortcuts and extension method that written for unity's built-in components. SkeletonAnimation is not supported by DoTween extension methods. You can tween its color property like this:
Color yourColor = Color.white; //GetComponent<SkeletonAnimation>().Skeleton.color
Color targetColor = Color.black;
float duration = 1f;
DOTween.To(() => yourColor, co => { yourColor = co; }, targetColor, duration);
Also, You can write your own extension:
public static class MyExtensions{
public static Tweener DOColor(this SkeletonAnimation target,
Color endValue, float duration)
{
DOTween.To(() => target.color,
co => { target.color = co; },
endValue,
duration);
}
}
If you are working with UI, For SkeletonGraphic, following code works.
public static class Utilities
{
public static void DOColor(SkeletonGraphic skeletonGraphic, Color endValue, float duration)
{
DOTween.To(() => skeletonGraphic.color,
newColor => { skeletonGraphic.color = newColor; },
endValue,
duration);
}
}
And then you can call like:
private SkeletonGraphic _skeletonGraphic;
private void Start()
{
_skeletonGraphic = spineAnimationParent.GetComponentInChildren<SkeletonGraphic>();
}
Utilities.DOColor(_skeletonGraphic, Color.grey, 0.5f);
It is important to NOT pass the color by value (i.e. _skeletonGraphic.color as input of DOColor function would be bad), because since you want to change the color inside of your class, you need to pass reach reference in Utilities.DOColor().

Cannot figure out how the bounds of a Region work

I'm currently writing a CAD-like program for logic circuits (it's my first "graphics intensive" program ever). When I place a component on the schematic, let say an AND gate (which is Region class at its root), I want to be able to interact with it (select it, change its properties, etc). So far, so good. I can click on it and everything go well. However, if I click outside of it, the mouse click event still show the component as it source(!).
Digging a bit further, I put some traces in the mouse click handler and found out that getBoundsInLocal() and getBoundsInParent() return bounds that are around 50% larger than it should be. The getLayoutBounds(), getWidth() and getHeight() do return the correct value.
The pane onto which the components are laid out is a simple Pane object, but it uses setScaleX() and setScaleY() to implement zooming capabilities. I did try to disable them, with no luck.
public abstract class SchematicComponent
extends Region {
private Shape graphicShape = null;
public Shape getGraphicShape() {
if( isShapeDirty() ) {
if( graphicShape != null ) {
getChildren().remove( graphicShape );
}
graphicShape = createShape();
markShapeDirty( false );
if( graphicShape != null ) {
getChildren().add( graphicShape );
}
}
return graphicShape;
}
abstract protected Shape createShape();
}
abstract public class CircuitComponent
extends SchematicComponent {
}
abstract public class LogicGate
extends CircuitComponent {
#Override
protected void layoutChildren() {
super.layoutChildren();
Pin outPin;
final double inputLength = getInputPinsMaxLength();
// Layout the component around its center.
// NOTE: I did try to set the center offset to 0 with no luck.
Point2D centerOffset = getCenterPointOffset().multiply( -1 );
Shape gateShape = getGraphicShape();
if( gateShape != null ) {
gateShape.setLayoutX( centerOffset.getX() + inputLength );
gateShape.setLayoutY( centerOffset.getY() );
}
/* Layout the output pins. */
outPin = getOutputPin();
if( outPin != null ) {
outPin.layout();
outPin.setLayoutX( centerOffset.getX() + getWidth() );
outPin.setLayoutY( centerOffset.getY() + getHeight() / 2 );
}
/* Compute the first input pin location and the gap between each
pins */
double pinGap = 2;
double y;
if( getInputPins().size() == 2 ) {
y = centerOffset.getY() + getHeight() / 2 - 2;
pinGap = 4;
}
else {
y = centerOffset.getY() + ( getHeight() / 2 ) - getInputPins().size() + 1;
}
/* Layout the input pins */
for( Pin inPin : getInputPins() ) {
inPin.layout();
inPin.layoutXProperty().set( centerOffset.getX() );
inPin.layoutYProperty().set( y );
y += pinGap;
}
}
}
// The actual object placed on the schematic
public class AndGate
extends LogicGate {
#Override
protected double computePrefWidth( double height ) {
// NOTE: computeMin/MaxWidth methods call this one
double width = getSymbolWidth() + getInputPinsMaxLength();
double length = 0;
width += length;
if( getOutputPin().getLength() > 0 ) {
width += getOutputPin().getLength();
}
return width; // Always 16
}
#Override
protected double computePrefHeight( double width ) {
// NOTE: computeMin/MaxHeight methods call this one
return getSymbolHeight() + getExtraHeight(); // Always 10
}
#Override
protected Shape createShape() {
Path shape;
final double extraHeight = getExtraHeight();
final double inputLength = getInputPinsMaxLength();
final double outputLength = getOutputPin().getLength();
/* Width and Height of the symbol itself (i,e, excluding the
input/output pins */
final double width = getWidth() - inputLength - outputLength;
final double height = getHeight() - extraHeight;
/* Starting point */
double startX = 0;
double startY = extraHeight / 2;
ArrayList<PathElement> elements = new ArrayList<>();
elements.add( new MoveTo( startX, startY ) );
elements.add( new HLineTo( startX + ( width / 2 ) ) );
elements.add( new ArcTo( ( width / 2 ), // X radius
height / 2, // Y radius
180, // Angle 180°
startX + ( width / 2 ), // X position
startY + height, // Y position
false, // large arc
true ) ); // sweep
elements.add( new HLineTo( startX ) );
if( extraHeight > 0 ) {
/* The height of the input pins is larger than the height of
the shape so we need to add extra bar on top and bottom of
the shape.
*/
elements.add( new MoveTo( startX, 0 ) );
elements.add( new VLineTo( extraHeight + height ) );
}
else {
elements.add( new VLineTo( startY ) );
}
shape = new Path( elements );
shape.setStroke( getPenColor() );
shape.setStrokeWidth( getPenSize() );
shape.setStrokeLineJoin( StrokeLineJoin.ROUND );
shape.setStrokeLineCap( StrokeLineCap.ROUND );
shape.setFillRule( FillRule.NON_ZERO );
shape.setFill( getFillColor() );
return shape;
}
} // End: LogiGate
// SchematicView is the ScrollPane container that handles the events
public class SchematicView
extends ScrollPane {
/* Mouse handler inner class */
private class MouseEventHandler
implements EventHandler<MouseEvent> {
#Override
public void handle( MouseEvent event ) {
if( event.getEventType() == MouseEvent.MOUSE_CLICKED ) {
processMouseClicked( event );
}
else { /* ... more stuff ... */ }
}
private void processMouseClicked( MouseEvent event ) {
Object node = event.getSource();
SchematicSheet sheet = getSheet();
Bounds local = ( (Node) node ).getLayoutBounds();
Bounds local1 = ( (Node) node ).getBoundsInLocal();
Bounds parent = ( (Node) node ).getBoundsInParent();
// At this point, here is what I get:
// node.getHeight() = 10 -> Good
// local.getHeight() = 10 -> Good
// local1.getHeight() = 15.6499996... -> Not expected!
// parent.getHeight() = 15.6500015... -> Not expected!
/*... More stuff ... */
}
}
So at this point, I'm running of clues of what is going on. Where do these getBoundsInXXX() values come from? They doesn't match with the parent's scale values either. The same goes with getWidth(): I get 24.825000... instead of 16.
Looking at this, I understand why clicking outside the component works as if I clicked on it. Its bounds are about 50% bigger than what it should be.
I googled the damn thing and search some doc for almost 2 days now and I'm still baffled. I think I understand that getBoundsInXXX() methods do their own computation but could it be off by that much? I don't thing so. My best guess is that it is something inside the createShape() method but I just can't figure what it is.
Anyone has a clue of what is going on?
Many thanks for your help.
P.S.: This is my first post here, so hopefully I did it right ;)
I think I finally found the problem :)
Basically, the Pin custom shape was drawn in the negative part of X axis (wrong calculations, my bad!). My best guess is that somehow, Java notices that I drew outside the standard bounds and then added the extra space used to the bounds, hence, adding 50% to width, which matches the length of the Pin. Drawing it in the positive region seem to have fixed the problem.
I'm not 100% sure if that is the right answer, but it make sense and it is now working has expected.

Changing colour of material over time

Is there a way to change the colour of a material over time? I want the background of my 2D game to change from white to red in a nice smooth transition.
In Unity 5, you can set the "Albedo" colour. This is the property I am looking to change. I can do this through using this:
back.SetColor("_Color", new Color(255, 0, 0, 0.5f));
However, how can the colour slowly change instead of being so instant?
not tested but try it this way:
var threshold = 0.001f;
var speed = 5.0f;
function Start()
{
// Execution
StartCoroutine("ChangeToColor", new Color(1.0f, 0f, 0f));
}
IEnumerator ChangeToColor(Color newColor)
{
var getColor = back.GetColor("_Color");
while(((Vector4)getColor - (Vector4)newColor).magnitude < threshold)
{
back.SetColor("_Color", Vector4.Lerp(getColor, newColor, Time.deltaTime * speed));
yield return null;
}
}

Wrong result when using setPixel()

I'm dealing with a problem for a few days now with setPixel() on Texture2D.
What i'm doing is getting mouse position or touch position(on android), then using that in setPixel() with transparent color. But the result i'm getting occur elsewhere instead of exactly where the mouse is...
public class EarshPic : MonoBehaviour {
public SpriteRenderer sr;
public SpriteRenderer srO;
public Camera c;
// Use this for initialization
void Start () {
CreateCover();//This method is working fine
}
private void CreateCover()
{
Color color = new Color(0.5F, 0.5f, 0.5F, 1.0F);
int x = srO.sprite.texture.width;
int y = srO.sprite.texture.height;
Texture2D tmpTexture = new Texture2D(srO.sprite.texture.width,
srO.sprite.texture.height);
for (int i = 0; i < tmpTexture.width; i++)
{
for (int j = 0; j < tmpTexture.height; j++)
{
tmpTexture.SetPixel(i, j, color);
}
}
tmpTexture.Apply(true);
sr.sprite = Sprite.Create(tmpTexture, srO.sprite.rect,
new Vector2(0.5f, 0.5f),srO.sprite.pixelsPerUnit);
}
// I have problem in that method
// Vector2 v = mousePostion or touchpostion
void Eraser(Vector2 v)
{
Color color = new Color(0.5F, 0.5f, 0.5F, 0.0F);
sr.sprite.texture.SetPixel(v.x, v.y, color);
sr.sprite.texture.Apply(true);
}
// Update is called once per frame
void Update () {
if(Input.mousePosition!=null)
{
Eraser(Input.mousePosition);
}
if (Input.touchCount == 1)
{
Touch touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Moved:
Eraser(touch.position);
break;
}
}
}
}
Problem
You are mixing different coordinates. This is the case if the texture is not perfectly screen sized. Your click is in screen coordinates and you are using it to set the transparency in texture coordinates.
Solution
This one requires the use of 3D models with colliders and textures on them. For 2D scenario you can use a box and set its texture to your 2D sprite. I don't know any easier method, but hopefully there is.
You have to first convert the screen position to world coordinate ray. This can be done with Camera.ScreenPointToRay.
Then you need to Physics.Raycast that ray to chech which position of the 3d model's collider it is intersecting with.
The intersection point can be changed to texture coordinates with RaycastHit.textureCoord. In the previous link, you can find a complete code example of the whole process.

Andengine Live wallpaper : Loosing textures on low end device

I am developing a live wallpaper using Andengine GLES2 Anchor centre branch ,based on the Development Cookbook.Wallpaper works fine on mid range to high end devices but shows problem on low end devices.I have tested it on Samsung galaxy ace , Micromax Funbook tablet and the issue generator Samsung galaxy Y. Issue found only on Samsung galaxy Y the only low end device I have.
Issue
I got loosing textures of all sprites when unlocking screens sometimes,or when returning to homepage some times,Error is not generated in a predictable manner, some times it doesn't cause any issue at all, But when it occurs to make my work even in my preview mode I have to force close the application and start the app again.
These are the details of my live wallpaper,
Wallpaper have A background sprite,A main image sprite ,two BatchedSpriteParticleSystem with some initializers and modifiers
I have a sepretae folder in asset for lower end device (320*480) where I keep small images and load all images to a single texture atlas in that case other wise I am using two texture atlas one for background image,and one for my main image and the two particle images.I am using a resource manager calss as per the andengine cookbook to load textures
Please help me to sort out the issue,I dont know where iam going wrong on this
here is my code ...
LiveWallpaperExtensionService given below
LiveWallpaperExtensionService
#TargetApi(13)
public class LiveWallpaperExtensionService extends BaseLiveWallpaperService {
public Sprite bg_Sprite;
public Sprite main_image_sprite;
public SpriteBackground background;
public BatchedSpriteParticleSystem beamParticleSystem;
public BatchedSpriteParticleSystem starParticleSystem;
private Camera mCamera;
private Scene mScene;
#Override
public org.andengine.engine.Engine onCreateEngine(
final EngineOptions pEngineOptions) {
return new FixedStepEngine(pEngineOptions, 50);
}
public EngineOptions onCreateEngineOptions() {
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE))
.getDefaultDisplay();
Utils.setGlobalWidthandHeight(Utils.getDisplaySize(display));
mCamera = new Camera(0, 0, Global.Width, Global.Height);
EngineOptions engineOptions = new EngineOptions(true,
ScreenOrientation.PORTRAIT_SENSOR, new FillResolutionPolicy(),
mCamera);
engineOptions.getRenderOptions().setDithering(true);
return engineOptions;
}
public void onCreateResources(
OnCreateResourcesCallback pOnCreateResourcesCallback) {
System.out.println("On create resourses");
ResourceManager.getInstance().loadBlueTextures(mEngine, this);
pOnCreateResourcesCallback.onCreateResourcesFinished();
}
public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback) {
System.out.println("On create scene");
mScene = new Scene();
pOnCreateSceneCallback.onCreateSceneFinished(mScene);
}
public void onPopulateScene(Scene arg0,
OnPopulateSceneCallback pOnPopulateSceneCallback) {
System.out.println("on populate ");
final float positionX = Global.Width * 0.5f;
final float positionY = Global.Height * 0.5f;
bg_Sprite = new Sprite(positionX, positionY,
ResourceManager.getInstance().mBackgroundTextureRegion,
this.getVertexBufferObjectManager());
main_image_sprite = new Sprite(positionX, positionY,
ResourceManager.getInstance().mJesusTextureRegion,
this.getVertexBufferObjectManager());
/*
* Define the center point of the particle system spawn location
*/
final int bparticleSpawnCenterX = (int) (Global.Width * 0.5f);
final int bparticleSpawnCenterY = (int) ((Global.Height * 0.5f) + ((Global.Height * 0.5f)) * 0.5f) - 25;
/* Define the radius of the circle for the particle emitter */
final float particleEmitterRadius = 50;
/* Create the particle emitter */
CircleOutlineParticleEmitter bparticleEmitter = new CircleOutlineParticleEmitter(
bparticleSpawnCenterX, bparticleSpawnCenterY,
particleEmitterRadius);
beamParticleSystem = new BatchedSpriteParticleSystem(bparticleEmitter,
10, 15, 50, ResourceManager.getInstance().mBeamTextureRegion,
mEngine.getVertexBufferObjectManager());
beamParticleSystem
.addParticleInitializer(new ExpireParticleInitializer<UncoloredSprite>(
3));
beamParticleSystem
.addParticleInitializer(new AccelerationParticleInitializer<UncoloredSprite>(
-150, 150, -150, 150));
RectangleParticleEmitter particleEmitter = new RectangleParticleEmitter(
((int) (Global.Width * 0.5f)), ((int) (Global.Height * 0.5f)),
Global.Width, Global.Height);
// Create a batched particle system for efficiency
starParticleSystem = new BatchedSpriteParticleSystem(particleEmitter,
1, 2, 20, ResourceManager.getInstance().mStarTextureRegion,
mEngine.getVertexBufferObjectManager());
/* Add an acceleration initializer to the particle system */
starParticleSystem
.addParticleInitializer(new ExpireParticleInitializer<UncoloredSprite>(
10));
starParticleSystem
.addParticleInitializer(new RotationParticleInitializer<UncoloredSprite>(
0, 160));
/* Define min/max values for the particle's scale */
starParticleSystem
.addParticleInitializer(new ScaleParticleInitializer<UncoloredSprite>(
0.3f, 1.5f));
/* Define the alpha modifier's properties */
starParticleSystem
.addParticleModifier(new AlphaParticleModifier<UncoloredSprite>(
0, 2, 0, 1));
/* Define the rotation modifier's properties */
starParticleSystem
.addParticleModifier(new RotationParticleModifier<UncoloredSprite>(
1, 9, 0, 180));
// Add alpha ('fade out') modifier
starParticleSystem
.addParticleModifier(new AlphaParticleModifier<UncoloredSprite>(
8, 10, 1, 0));
/*
* Create the SpriteBackground object, specifying the color values &
* Sprite object to display
*/
final float red = 0.7f;
final float green = 0.78f;
final float blue = 0.85f;
final float alpha = 1;
background = new SpriteBackground(red, green, blue, bg_Sprite);
mScene.setBackground(background);
mScene.setBackgroundEnabled(true);
// Attach our particle system to the scene
mScene.attachChild(starParticleSystem);
mScene.attachChild(beamParticleSystem);
mScene.attachChild(main_image_sprite);
bg_Sprite.setIgnoreUpdate(true);
main_image_sprite.setIgnoreUpdate(true);
pOnPopulateSceneCallback.onPopulateSceneFinished();
}
#Override
protected synchronized void onPause() {
System.out.println("On paused");
super.onPause();
if (starParticleSystem != null) {
starParticleSystem.setParticlesSpawnEnabled(false);
}
if (beamParticleSystem != null) {
beamParticleSystem.setParticlesSpawnEnabled(false);
}
}
#Override
protected synchronized void onResume() {
System.out.println("On resume");
super.onResume();
if (starParticleSystem != null) {
starParticleSystem.setParticlesSpawnEnabled(true);
}
if (beamParticleSystem != null) {
beamParticleSystem.setParticlesSpawnEnabled(true);
}
}
}
}
Please help me to sort out this issue ,I welcomes all ideas Suggestions, Any thing any thoughts of you to solve this issue ....
I've noticed that Galaxy Y has lots of issues, been getting lots of crash reports for a game of mine till I blocked them from downloading it and all reports stopped [it was the only device with issues]
I suggest you do the same
edit:
if you want to chose which devices to support, you can use this example
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="false"
android:anyDensity="true" />
modify that as you see fit