How to set shader for Armature object in Cocos2dx - cocos2d-x

I have 3 questions:
I know when I want to set shader for Sprite object by using spr1->setShaderProgram(glProgram). However, I want to set shader for an Armature object. How can I do that?
In CCSprite, I can use setBlendFunc, what about in Armature.
I read this article http://blog.muditjaju.infiniteeurekas.in/?p=1 and I see the idea to detect collision between 2 sprites. But I want to write a extended function which can detect collision between different object like sprite vs sprite, sprite vs Armature, Amature vs Armature. How can I do that?
Thanks

Today I met the same situation like you. And luckily I found a solution from someone else. The main point is to write a subclass of Armature, say ShaderArmature, and rendering every bone's every child with a shader ---- in my case, the shader is for graying the armature.
You can create a ShaderArmature object and call its setgrayState() to enable graying and setUsuaState() to remove graying effect.
1, Add the ShaderArmature.h & ShaderArmature.cpp in the same directory of Armature.h.
ShaderArmature.h
//
// Created by Wangyq on 2017/8/13.
//
//
#ifndef __SHADERARMATURE_H__
#define __SHADERARMATURE_H__
//
//---------shaderArmature.h
///
#include "cocos2d.h"
//#include "extensions/cocos-ext.h"
#include "cocostudio/CocoStudio.h"
//USING_NS_CC_EXT;
USING_NS_CC;
namespace cocostudio{
class CC_STUDIO_DLL ShaderArmature : public Armature
{
public:
ShaderArmature();
virtual ~ShaderArmature();
bool init(const std::string& name) override;
static ShaderArmature *create(const std::string& name) ;
virtual void initShader(bool shaderState);
// void setBackgroundNotification();
// draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags)
virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t transformUpdated) override;
// void listenBackToForeground(Ref *obj);
void setIceState();
void setUsuaState();
void setblurState();
void setbanishState();
void setfrozenState();
void setgrayState();
void setinvisState();
void setmirrorState();
void setpoisonState();
void setstoneState();
protected:
std::string _fragSourceFile;
std::string _vertSourceFile;
//GLchar * fragSource;
//GLchar * vertSource;
GLchar fragSource[2048];
GLchar vertSource[2048];
bool bSetShader;
};
}
#endif /* __SHADERARMATURE_H__ */
ShaderArmature.cpp
//
// ShaderArmature.cpp
// cocos2d_libs
//
// Created by Wangyq on 2017/8/13.
//
//
#include "ShaderArmature.h"
using namespace cocos2d;
namespace cocostudio {
ShaderArmature::ShaderArmature(){
}
ShaderArmature::~ShaderArmature(){
}
bool ShaderArmature::init(const std::string& name)
{
return Armature::init(name);
}
ShaderArmature *ShaderArmature::create(const std::string& name)
{
ShaderArmature *armature = new ShaderArmature;
if (armature && armature->init(name))
{
armature->autorelease();
return armature;
}
else{
CC_SAFE_DELETE(armature);
return nullptr;
}
}
void ShaderArmature::initShader(bool shaderState){
//Traverse every bone to set shader
for (auto& object : _boneDic)
{
if (Bone *bone = dynamic_cast<Bone *>(object.second))
{
if (bone == nullptr)
continue;
//-----------------!! IMPORTANT !!-----------------
//Each bone may have more than one child, like several frames, also need to assign shader to them
//Without this step, only the first frame will have the shader effect.
const Vector<DecorativeDisplay*> list = bone->getDisplayManager()->getDecorativeDisplayList();
for (auto& display : list)
{
Node* node = display->getDisplay();
if (node == nullptr)
continue;
if (shaderState){
auto program = new GLProgram();
program->initWithByteArrays(vertSource, fragSource);
node->setGLProgram(program);
program->autorelease();
CHECK_GL_ERROR_DEBUG();
program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
CHECK_GL_ERROR_DEBUG();
program->link();
CHECK_GL_ERROR_DEBUG();
program->updateUniforms();
CHECK_GL_ERROR_DEBUG();
}
else{//addNormal shader
node->setGLProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP));
}
}
}
}
}
void ShaderArmature::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
Armature::draw(renderer, transform, flags);
}
void ShaderArmature::setIceState(){
_fragSourceFile = "shader/IceShader.fsh";
_vertSourceFile = "shader/IceShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setUsuaState(){
initShader(false);
}
void ShaderArmature::setblurState(){
_fragSourceFile = "shader/Blur.fsh";
_vertSourceFile = "shader/Blur.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setbanishState(){
_fragSourceFile = "shader/BanishShader.fsh";
_vertSourceFile = "shader/BanishShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setfrozenState(){
_fragSourceFile = "shader/FrozenShader.fsh";
_vertSourceFile = "shader/FrozenShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setgrayState(){
_fragSourceFile = "shader/GrayScalingShader.fsh";
_vertSourceFile = "shader/GrayScalingShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setinvisState(){
_fragSourceFile = "shader/InvisibleShader.fsh";
_vertSourceFile = "shader/InvisibleShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setmirrorState(){
_fragSourceFile = "shader/MirrorShader.fsh";
_vertSourceFile = "shader/MirrorShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setpoisonState(){
_fragSourceFile = "shader/PoisonShader.fsh";
_vertSourceFile = "shader/PoisonShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
void ShaderArmature::setstoneState(){
_fragSourceFile = "shader/StoneShader.fsh";
_vertSourceFile = "shader/StoneShader.vsh";
fragSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_fragSourceFile).c_str())->getCString();
vertSource = (GLchar*)String::createWithContentsOfFile(
FileUtils::getInstance()->fullPathForFilename(_vertSourceFile).c_str())->getCString();
initShader(true);
}
}
2, For shader files, I put them in the folder "res/shader". Every shader consists of 2 files -- a .vsh file & a .fsh file. Take graying for example:
GrayScalingShader.fsh
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform sampler2D u_texture;
void main()
{
vec4 normalColor = v_fragmentColor * texture2D(u_texture, v_texCoord);
//float gray = 0.299*0.5*normalColor.r + 0.587*0.5*normalColor.g + 0.114*0.5*normalColor.b;
//gl_FragColor = vec4(gray*0.8 + normalColor.r*0.2, gray*0.8 + normalColor.g*0.2, gray*0.8 + normalColor.b*0.2, normalColor.a*0.3);
float gray = dot(normalColor.rgb, vec3(0.299 * 0.5, 0.587 * 0.5, 0.114 * 0.5));
gl_FragColor = vec4(gray, gray, gray, normalColor.a * 1);
}
GrayScalingShader.vsh
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif
void main()
{
//gl_Position = CC_MVPMatrix * a_position;
gl_Position = CC_PMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}
Hope this would be helpful.

Related

How to show marker with text in map in codenameone

I am doing a sample on Map and in this i want to show marker along with text.
for that i used below code.
MapContainer mapContainer = new MapContainer("xxxxxxxxxxxxxxxxxxxxxx");
Coord coord = new Coord(latitude,longitude);
mapContainer.setCameraPosition(coord);
mapContainer.addMarker(EncodedImage.createFromImage(image,false), mapContainer.getCameraPosition(), "Text", "Text", null);
but this code helps me to displaying the marker but not along with the text. so if anyone have idea to display marker with text please suggest/help me to achieve this.
Thanks in Advance.
here is the code using MapContainer and MapLayout(Below Answer)..
if(BrowserComponent.isNativeBrowserSupported()) {
MapContainer mc = new MapContainer("AIzaSyDLIu4RfdXVQPvRqOYLP6N8ocCQpPNqtIk");
mapDemo.add(mc);
Container markers = new Container();
markers.setLayout(new MapLayout(mc, markers));
mapDemo.add(markers);
Coord moscone = new Coord(37.7831, -122.401558);
Button mosconeButton = new Button("");
FontImage.setMaterialIcon(mosconeButton, FontImage.MATERIAL_PLACE);
markers.add(moscone, mosconeButton);
Coord moscone1 = new Coord(36.6139, -120.2090);
Button mosconeButton1 = new Button("");
FontImage.setMaterialIcon(mosconeButton1, FontImage.MATERIAL_PLACE);
markers.add(moscone1, mosconeButton1);
mc.zoom(moscone1, 5);
} else {
// iOS Screenshot process...
mapDemo.add(new Label("Loading, please wait...."));
}
This is a revised answer see the original answer for reference below:
With the new update this should work better, you will need to update the cn1lib for Google Maps too if you have an older version. We've also revised the MapLayout class again and added some features such as alignment:
public class MapLayout extends Layout implements MapListener {
private static final String COORD_KEY = "$coord";
private static final String POINT_KEY = "$point";
private static final String HORIZONTAL_ALIGNMENT = "$align";
private static final String VERTICAL_ALIGNMENT = "$valign";
private final MapContainer map;
private final Container actual;
private boolean inUpdate;
private Runnable nextUpdate;
private int updateCounter;
public static enum HALIGN {
LEFT {
#Override
int convert(int x, int width) {
return x;
}
},
CENTER {
#Override
int convert(int x, int width) {
return x - width / 2;
}
},
RIGHT {
#Override
int convert(int x, int width) {
return x - width;
}
};
abstract int convert(int x, int width);
}
public static enum VALIGN {
TOP {
#Override
int convert(int y, int height) {
return y;
}
},
MIDDLE {
#Override
int convert(int y, int height) {
return y + height / 2;
}
},
BOTTOM {
#Override
int convert(int y, int height) {
return y + height;
}
};
abstract int convert(int y, int height);
}
public MapLayout(MapContainer map, Container actual) {
this.map = map;
this.actual = actual;
map.addMapListener(this);
}
#Override
public void addLayoutComponent(Object value, Component comp, Container c) {
comp.putClientProperty(COORD_KEY, (Coord) value);
}
#Override
public boolean isConstraintTracking() {
return true;
}
#Override
public Object getComponentConstraint(Component comp) {
return comp.getClientProperty(COORD_KEY);
}
#Override
public boolean isOverlapSupported() {
return true;
}
public static void setHorizontalAlignment(Component cmp, HALIGN a) {
cmp.putClientProperty(HORIZONTAL_ALIGNMENT, a);
}
public static void setVerticalAlignment(Component cmp, VALIGN a) {
cmp.putClientProperty(VERTICAL_ALIGNMENT, a);
}
#Override
public void layoutContainer(Container parent) {
int parentX = 0;
int parentY = 0;
for (Component current : parent) {
Coord crd = (Coord) current.getClientProperty(COORD_KEY);
Point p = (Point) current.getClientProperty(POINT_KEY);
if (p == null) {
p = map.getScreenCoordinate(crd);
current.putClientProperty(POINT_KEY, p);
}
HALIGN h = (HALIGN)current.getClientProperty(HORIZONTAL_ALIGNMENT);
if(h == null) {
h = HALIGN.LEFT;
}
VALIGN v = (VALIGN)current.getClientProperty(VERTICAL_ALIGNMENT);
if(v == null) {
v = VALIGN.TOP;
}
current.setSize(current.getPreferredSize());
current.setX(h.convert(p.getX() - parentX, current.getWidth()));
current.setY(v.convert(p.getY() - parentY, current.getHeight()));
}
}
#Override
public Dimension getPreferredSize(Container parent) {
return new Dimension(100, 100);
}
#Override
public void mapPositionUpdated(Component source, int zoom, Coord center) {
Runnable r = new Runnable() {
public void run() {
inUpdate = true;
try {
List<Coord> coords = new ArrayList<>();
List<Component> cmps = new ArrayList<>();
int len = actual.getComponentCount();
for (Component current : actual) {
Coord crd = (Coord) current.getClientProperty(COORD_KEY);
coords.add(crd);
cmps.add(current);
}
int startingUpdateCounter = ++updateCounter;
List<Point> points = map.getScreenCoordinates(coords);
if (startingUpdateCounter != updateCounter || len != points.size()) {
// Another update must have run while we were waiting for the bounding box.
// in which case, that update would be more recent than this one.
return;
}
for (int i=0; i<len; i++) {
Component current = cmps.get(i);
Point p = points.get(i);
current.putClientProperty(POINT_KEY, p);
}
actual.setShouldCalcPreferredSize(true);
actual.revalidate();
if (nextUpdate != null) {
Runnable nex = nextUpdate;
nextUpdate = null;
callSerially(nex);
}
} finally {
inUpdate = false;
}
}
};
if (inUpdate) {
nextUpdate = r;
} else {
nextUpdate = null;
callSerially(r);
}
}
}
Original Answer:
The marker will show the text when you tap on it. If you want things to appear differently you can just use any component and place it on top of the map. The upcoming Uber tutorial will cover this in depth but I explained the basic system in this blog post: https://www.codenameone.com/blog/tip-map-layout-manager.html

Easy way to write and read some Transform to a text file in Unity3d?

This is strictly in Unity3D, I have an array of 100 Transform,
I want to write those to a file on the PC desktop, and later read them.
Consider ...
// write to desktop...
string s = "";
for ( int i=0; i< sendMe.Length; ++i )
{
Transform tt = sendMe[i].transform;
s = s +tt.position.x +"," +tt.position.x +"," ...etc... "\n";
}
string d = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.DesktopDirectory);
string path = System.IO.Path.Combine(d, "happyData.txt" );
System.IO.File.Delete(path);
System.IO.File.WriteAllText(path,s);
return;
.. and then read it similarly, just manually parsing the text format. Something like...
public ReadTrackFromDesktopFile()
{
GetSplineGetReady("development");
string tt = File.ReadAllText( .. path as above);
List<string> ts = new List<string>(
tt.Split(new string[] { "\r","\n" },
StringSplitOptions.RemoveEmptyEntries) );
foreach (string t in ts)
{
string[] parts = t.Split(',');
Vector3 pos = new Vector3(parts[0],parts[1],parts[2]);
Quaternion rot = new Quaternion(parts[3],parts[4],parts[5],parts[6]);
GetSplineOneNode(pos, rot);
}
GetSplineFinalise();
}
But this seems naive. What's the "simple Unity way" to do this?
You can use Json and the Playerprefs to save the Transform. This is a TransformSaver extension class that can do that:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public static class TransformSaver
{
[System.Serializable]
public class TransformInfo
{
public Vector3 pos;
public Quaternion rot;
public Vector3 scale;
}
//Save Transform
public static void SaveTransform(this Transform trans, Transform[] tranformToSave)
{
TransformInfo[] trnfrm = new TransformInfo[tranformToSave.Length];
for (int i = 0; i < trnfrm.Length; i++)
{
trnfrm[i] = new TransformInfo();
trnfrm[i].pos = tranformToSave[i].position;
trnfrm[i].rot = tranformToSave[i].rotation;
trnfrm[i].scale = tranformToSave[i].localScale;
}
string jsonTransform = JsonHelper.ToJson(trnfrm, true);
PlayerPrefs.SetString("transform", jsonTransform);
}
//Load Transform
public static Transform[] LoadTransform(this Transform trans)
{
string jsonTransform = PlayerPrefs.GetString("transform");
if (jsonTransform == null)
{
return null;
}
//Debug.Log("Loaded: " + jsonTransform);
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
gameObjects[i] = new GameObject("_");
gameObjects[i].hideFlags = HideFlags.HideAndDontSave;
loadedTransforms[i] = gameObjects[i].transform;
loadedTransforms[i].position = savedTransforms[i].pos;
loadedTransforms[i].rotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
public static void CopyTransform(this Transform trans, Transform source, Transform target, bool createNewInstance = false)
{
if (source == null)
{
return;
}
if (target == null || createNewInstance)
{
GameObject obj = new GameObject("_");
obj.hideFlags = HideFlags.HideAndDontSave;
target = obj.transform;
}
target.position = source.position;
target.rotation = source.rotation;
target.localScale = source.localScale;
}
public static void CopyTransform(this Transform trans, Transform[] source, Transform[] target, bool createNewInstance = false)
{
if (source == null || source.Length <= 0)
{
return;
}
for (int i = 0; i < target.Length; i++)
{
CopyTransform(null, source[i], target[i], createNewInstance);
if (i >= target.Length - 1)
{
break;
}
}
}
}
JsonHelper script for converting arrays to json and vice versa:
using UnityEngine;
using System.Collections;
using System;
public class JsonHelper
{
public static T[] FromJson<T>(string json)
{
Wrapper<T> wrapper = UnityEngine.JsonUtility.FromJson<Wrapper<T>>(json);
return wrapper.Items;
}
public static string ToJson<T>(T[] array, bool prettyPrint)
{
Wrapper<T> wrapper = new Wrapper<T>();
wrapper.Items = array;
return UnityEngine.JsonUtility.ToJson(wrapper, prettyPrint);
}
[Serializable]
private class Wrapper<T>
{
public T[] Items;
}
}
Usage:
using UnityEngine;
using System.Collections;
public class TransformTest : MonoBehaviour
{
public Transform[] objectToSave;
// Use this for initialization
void Start()
{
//Save Transform
transform.SaveTransform(objectToSave);
//Load Transform
Transform[] loadedTransform = transform.LoadTransform();
transform.CopyTransform(loadedTransform, objectToSave);
}
}
It's not perfect and can be improved. Improvement includes making the LoadTransform function take transform array as parameter instead of returning an array.
You can use Binary Serialization.
Create following structs:
[Serializable]
public struct SerializebleVector
{
public float x, y, z, w;
public SerializebleVector(float x, float y, float z, float w = 0f)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public static explicit operator SerializebleVector(Quaternion a)
{
return new SerializebleVector(a.x, a.y, a.z, a.w);
}
public static implicit operator SerializebleVector(Vector3 a)
{
return new SerializebleVector(a.x, a.y, a.z);
}
}
[Serializable]
public struct SerializebleTransform
{
SerializebleVector position;
SerializebleVector rotation;
SerializebleVector scale;
public SerializebleTransform(Transform tr)
{
position = tr.position;
rotation = (SerializebleVector) tr.rotation;
scale = tr.lossyScale;
}
public static implicit operator SerializebleTransform(Transform tr)
{
return new SerializebleTransform(tr);
}
}
Save method:
public static void Save(Transform[] ts )
{
var data = new List<SerializebleTransform>();
foreach (var t in ts)
{
data.Add(t);
}
BinaryFormatter bf = new BinaryFormatter();
using (FileStream file = File.Create (Application.persistentDataPath + "/savedTransforms.dat"))
{
bf.Serialize(file, data);
}
}
Load Method:
public static void Load(out List<SerializebleTransform> ts)
{
if(File.Exists(Application.persistentDataPath + "/savedTransforms.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
using (FileStream file = File.Open(Application.persistentDataPath + "/savedTransforms.dat", FileMode.Open))
{
var data = (List<SerializebleTransform>)bf.Deserialize(file);
return data;
}
}
else
ts = null;
}
PS: As far as I know you can't create Transform without GameObject, so read data from SerializebleTransform into Transform kind of manually.

Cocos2d-x RenderTexture the image is black(empty) on Android's gallery

In my app , i'm using a RenderTexture as a Canvas.
I would like to save this canvas to android photo gallery , like this:
The cpp side :
void Canvas::saveCanvasCallback(cocos2d::Ref *sender)
{
time_t t = time(0);
struct tm * timeinfo= localtime (&t);
char buffer[120];
strftime(buffer,120,"image_%d%m%Y%I%M%S.png",timeinfo);
auto callback = [&](RenderTexture* rt, const std::string& path)
{
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
addImageToGallery(path.c_str());
#else
CGameManager_iOSBridge::addImageToGallery(path.c_str());
#endif
};
_target->saveToFile(buffer, Image::Format::PNG, true, callback);
CCLOG("Image saved %s \n", buffer);
}
The java side :
static void addImageToGallery (String path) {
ContentValues values = new ContentValues();
values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.MediaColumns.DATA, Environment.getExternalStorageDirectory().getPath()+path);
getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
me.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(me.getApplicationContext(), "Image saved ",
Toast.LENGTH_LONG).show();
}
});
}
The saved image it's black(empty) in the gallery ( for iOS it works fine) .
I'm working with cocos2d-x V3.3
Please help me
Thanks

How to use fragment shader with cocos2d-x 3.1?

The code below is from this tutorial: http://www.raywenderlich.com/10862/how-to-create-cool-effects-with-custom-shaders-in-opengl-es-2-0-and-cocos2d-2-x
It is a very cool tutorial, but I don't know how to do that in cocos2d-x 3. I have translated the code below
- (id)init
{
self = [super init];
if (self) {
// 1
sprite = [CCSprite spriteWithFile:#"Default.png"];
sprite.anchorPoint = CGPointZero;
sprite.rotation = 90;
sprite.position = ccp(0, 320);
[self addChild:sprite];
// 2
const GLchar * fragmentSource = (GLchar*) [[NSString stringWithContentsOfFile:[CCFileUtils fullPathFromRelativePath:#"CSEColorRamp.fsh"] encoding:NSUTF8StringEncoding error:nil] UTF8String];
sprite.shaderProgram = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTextureA8Color_vert
fragmentShaderByteArray:fragmentSource];
[sprite.shaderProgram addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position];
[sprite.shaderProgram addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords];
[sprite.shaderProgram link];
[sprite.shaderProgram updateUniforms];
// 3
colorRampUniformLocation = glGetUniformLocation(sprite.shaderProgram->program_, "u_colorRampTexture");
glUniform1i(colorRampUniformLocation, 1);
// 4
colorRampTexture = [[CCTextureCache sharedTextureCache] addImage:#"colorRamp.png"];
[colorRampTexture setAliasTexParameters];
// 5
[sprite.shaderProgram use];
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, [colorRampTexture name]);
glActiveTexture(GL_TEXTURE0);
}
return self;
}
and obtained this:
Vec2 origin = Director::getInstance()->getVisibleOrigin();
sprite = Sprite::create("HelloWorld.png");
sprite->setAnchorPoint(Vec2(0, 0));
sprite->setRotation(3);
sprite->setPosition(origin);
addChild(sprite);
const GLchar * fragmentSource = FileUtils::getInstance()->getStringFromFile("CSEColorRamp.fsh").c_str();
GLProgram* p = GLProgram::createWithByteArrays(ccPositionTextureA8Color_vert, fragmentSource);
sprite->setGLProgram(p);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
p->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD);
p->link();
p->updateUniforms();
// 3
colorRampUniformLocation = glGetUniformLocation(sprite->getGLProgram()->getProgram(), "u_colorRampTexture");
glUniform1i(colorRampUniformLocation, 1);
// 4
colorRampTexture = Director::getInstance()->getTextureCache()->addImage("colorRamp.png");
colorRampTexture->setAliasTexParameters();
// 5
sprite->getGLProgram()->use();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, colorRampTexture->getName());
glActiveTexture(GL_TEXTURE0);
But it does not work. It shows a black screen with 2 draw calls. What is wrong? Did I pass all uniforms and attributes normally to the fragment shader. Did I initialize the program correctly?
Here is an example how shader should be used in cocos2d-x 3.1:
.h file
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
using namespace cocos2d;
class HelloWorld : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
virtual void visit(Renderer *renderer, const Mat4 &transform, bool transformUpdated) override;
//we call our actual opengl commands here
void onDraw();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
private:
CustomCommand _customCommand;
GLuint vao;
GLuint vertexVBO;
GLuint colorVBO;
};
#endif // __HELLOWORLD_SCENE_H__
.cpp file
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
//create my own program
auto program = new GLProgram;
program->initWithFilenames("myVertextShader.vert", "myFragmentShader.frag");
program->link();
//set uniform locations
program->updateUniforms();
// this->setGLProgram(GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_COLOR));
this->setGLProgram(program);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
typedef struct {
float Position[2];
float Color[4];
} Vertex;
// auto size = Director::getInstance()->getVisibleSize();
Vertex data[] =
{
{{-1,-1},{0,1,0,1}},
{{1,-1},{1,0,0,1}},
{ {-1,1},{0,0,1,1}},
{{1,1},{0,1,0,1}}
};
GLubyte indices[] = { 0,1,2, //第一个三角形索引
2,3,1}; //第二个三角形索引
glGenBuffers(1, &vertexVBO);
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
GLuint positionLocation = glGetAttribLocation(program->getProgram(), "a_position");
// CCLOG("position =%d", positionLocation);
glEnableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation,
2,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
(GLvoid*)offsetof(Vertex,Position));
//set for color
// glGenBuffers(1, &colorVBO);
// glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
// glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
GLuint colorLocation = glGetAttribLocation(program->getProgram(), "a_color");
glEnableVertexAttribArray(colorLocation);
glVertexAttribPointer(colorLocation,
4,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
(GLvoid*)offsetof(Vertex,Color));
GLuint indexVBO;
glGenBuffers(1, &indexVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices) , indices, GL_STATIC_DRAW);
program->autorelease();
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 使用vao http://blog.sina.com.cn/s/blog_4a657c5a01016f8s.html
return true;
}
void HelloWorld::visit(cocos2d::Renderer *renderer, const Mat4 &transform, bool transformUpdated)
{
Layer::draw(renderer, transform, transformUpdated);
//send custom command to tell the renderer to call opengl commands
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
renderer->addCommand(&_customCommand);
}
void HelloWorld::onDraw()
{
//question1: why the triangle goes to the up side
//如果使用对等矩阵,则三角形绘制会在最前面
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
auto glProgram = getGLProgram();
glProgram->use();
//set uniform values, the order of the line is very important
glProgram->setUniformsForBuiltins();
auto size = Director::getInstance()->getWinSize();
//use vao
glBindVertexArray(vao);
GLuint uColorLocation = glGetUniformLocation(glProgram->getProgram(), "u_color");
float uColor[] = {1.0, 0.0, 0.0, 1.0};
glUniform4fv(uColorLocation,1, uColor);
// glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE,(GLvoid*)0);
glBindVertexArray(0);
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 6);
CHECK_GL_ERROR_DEBUG();
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
myVertextShader.vert vertex shader
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 v_fragmentColor;
uniform vec4 u_color;
void main()
{
gl_Position = CC_MVPMatrix * a_position;
v_fragmentColor = a_color * u_color;
}
myFragmentShader.frag fragment shader
varying vec4 v_fragmentColor;
void main()
{
gl_FragColor = v_fragmentColor;
}
I have used this source: http://4gamers.cn/blog/categories/opengl-es/

How to insert text in CCTextFieldTTF?

I used setString but the string is not updated, so I have to write a CCLabel to show the string, which I feel very weird because showing the user input should be part of the textfield.. Do I missed anything?
I read the test_input example, it uses a CCLabel to show the user input, which I think is a really bad design.
You don't need a Label to show user input.
I have edited the default HelloWorld file with added CCTextFieldttf working example.
This is how your HelloWorld.h file should look
class HelloWorld : public cocos2d::CCLayer, public cocos2d::CCTextFieldDelegate
{
public:
virtual bool init();
static cocos2d::CCScene* scene();
void createTF();
cocos2d::CCTextFieldTTF* tf;
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void registerWithTouchDispatcher();
//CCtextFieldttf delegate begin
virtual bool onTextFieldAttachWithIME(CCTextFieldTTF * pSender);
virtual bool onTextFieldDetachWithIME(CCTextFieldTTF * pSender);
virtual bool onTextFieldInsertText(CCTextFieldTTF * pSender, const char * text, int nLen);
virtual bool onTextFieldDeleteBackward(CCTextFieldTTF * pSender, const char * delText, int nLen);
virtual bool onDraw(CCTextFieldTTF * pSender);
//CCtextFieldttf delegate end
CREATE_FUNC(HelloWorld);
};
This is how your HelloWorld.cpp should be for minimum usage of CCTextFieldttf
CCScene* HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}
this->setTouchEnabled(true);
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
createTF();
CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);
// position the label on the center of the screen
pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - pLabel->getContentSize().height));
this->addChild(pLabel, 1);
return true;
}
void HelloWorld::createTF()
{
tf = CCTextFieldTTF::textFieldWithPlaceHolder("123", CCSizeMake(100, 100), kCCTextAlignmentCenter, "helvetica", 20);
tf->setColorSpaceHolder(ccWHITE);
tf->setPosition(ccp(200,200));
tf->setHorizontalAlignment(kCCTextAlignmentCenter);
tf->setVerticalAlignment(kCCVerticalTextAlignmentCenter);
tf->setDelegate(this);
addChild(tf);
}
void HelloWorld::registerWithTouchDispatcher()
{
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}
bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
CCLog("inside touchbegan");
return true;
}
void HelloWorld::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
CCLog("inside touchend");
tf->attachWithIME();
}
bool HelloWorld::onTextFieldAttachWithIME(CCTextFieldTTF * pSender)
{
return false;
}
bool HelloWorld::onTextFieldDetachWithIME(CCTextFieldTTF * pSender)
{
return false;
}
bool HelloWorld::onTextFieldInsertText(CCTextFieldTTF * pSender, const char * text, int nLen)
{
return false;
}
bool HelloWorld::onTextFieldDeleteBackward(CCTextFieldTTF * pSender, const char * delText, int nLen)
{
return false;
}
bool HelloWorld::onDraw(CCTextFieldTTF * pSender)
{
return false;
}
The CCTextFieldDelegate methods are implemented to gain more control over what is entered into CCTextFieldTTF. The only thing that CCTextFieldttf lacks is you have to call CCTextFieldTTF's attachWithIME() method yourself like in the above code it is being called in "ccTouchEnded".
in header
class CustomMultiplayerScene : public PZGBaseMenuScene,public CCIMEDelegate
{
public:
void keyboardWillShow(cocos2d::CCIMEKeyboardNotificationInfo &info);
void keyboardWillHide(cocos2d::CCIMEKeyboardNotificationInfo &info);
void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
};
in .cpp
CCTextFieldTTF *textfield = CCTextFieldTTF::textFieldWithPlaceHolder("Enter Name:", CCSize(480,30), kCCTextAlignmentCenter, "Arial", 12);
// textfield->setAnchorPoint(CCPointZero);
textfield->setPosition(ccp(screenSize.width/2,200));
textfield->setTag(100);
this->addChild(textfield,4);
CCLabelTTF *label = CCLabelTTF::create("ID : ", "", 12);
label->setPosition(ccp(screenSize.width/2,100));
label->setTag(200);
this->addChild(label,4);
implement methods in .cpp
void CustomMultiplayerScene::keyboardWillShow(CCIMEKeyboardNotificationInfo &info)
{
CCLOG("keyboardWillShow");
CCTextFieldTTF *textfield = (CCTextFieldTTF *)this->getChildByTag(100);
textfield->setString("");
}
void CustomMultiplayerScene::keyboardWillHide(CCIMEKeyboardNotificationInfo &info)
{
CCLog("keyboardWillHide");
CCTextFieldTTF *textfield = (CCTextFieldTTF *)this->getChildByTag(100);
CCLabelTTF *label = (CCLabelTTF *)this->getChildByTag(200);
label->setString(textfield->getString());
}
void CustomMultiplayerScene::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
CCTouch *pTouch = (CCTouch *)pTouches->anyObject();
CCPoint point = pTouch->getLocationInView();
point = CCDirector::sharedDirector()->convertToGL(point);
CCTextFieldTTF *textfield = (CCTextFieldTTF *)this->getChildByTag(100);
CCRect rect = textfield->boundingBox();
if(rect.containsPoint(point)) {
textfield->attachWithIME();
}
}
CCTextFieldTTF * pTextField = CCTextFieldTTF::textFieldWithPlaceHolder("click here for input",
"Thonburi",
20);
addChild(pTextField);