diff --git a/build/cocos2d_libs.xcodeproj/project.pbxproj b/build/cocos2d_libs.xcodeproj/project.pbxproj index 136d453c03..851b93cfaf 100644 --- a/build/cocos2d_libs.xcodeproj/project.pbxproj +++ b/build/cocos2d_libs.xcodeproj/project.pbxproj @@ -1452,6 +1452,10 @@ 503DD8F81926B0DB00CD74DD /* CCIMEDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 503DD8F31926B0DB00CD74DD /* CCIMEDispatcher.cpp */; }; 503DD8F91926B0DB00CD74DD /* CCIMEDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 503DD8F41926B0DB00CD74DD /* CCIMEDispatcher.h */; }; 503DD8FA1926B0DB00CD74DD /* CCIMEDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 503DD8F41926B0DB00CD74DD /* CCIMEDispatcher.h */; }; + 505385021B01887A00793096 /* CCProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = 505385001B01887A00793096 /* CCProperties.h */; }; + 505385031B01887A00793096 /* CCProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = 505385001B01887A00793096 /* CCProperties.h */; }; + 505385041B01887A00793096 /* CCProperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 505385011B01887A00793096 /* CCProperties.cpp */; }; + 505385051B01887A00793096 /* CCProperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 505385011B01887A00793096 /* CCProperties.cpp */; }; 50643BD419BFAECF00EF68ED /* CCGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 50643BD319BFAECF00EF68ED /* CCGL.h */; }; 50643BD519BFAECF00EF68ED /* CCGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 50643BD319BFAECF00EF68ED /* CCGL.h */; }; 50643BD619BFAEDA00EF68ED /* CCPlatformDefine.h in Headers */ = {isa = PBXBuildFile; fileRef = 5091A7A219BFABA800AC8789 /* CCPlatformDefine.h */; }; @@ -4238,6 +4242,8 @@ 503DD8F21926B0DB00CD74DD /* CCIMEDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCIMEDelegate.h; path = ../base/CCIMEDelegate.h; sourceTree = ""; }; 503DD8F31926B0DB00CD74DD /* CCIMEDispatcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CCIMEDispatcher.cpp; path = ../base/CCIMEDispatcher.cpp; sourceTree = ""; }; 503DD8F41926B0DB00CD74DD /* CCIMEDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCIMEDispatcher.h; path = ../base/CCIMEDispatcher.h; sourceTree = ""; }; + 505385001B01887A00793096 /* CCProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCProperties.h; path = ../base/CCProperties.h; sourceTree = ""; }; + 505385011B01887A00793096 /* CCProperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CCProperties.cpp; path = ../base/CCProperties.cpp; sourceTree = ""; }; 50643BD319BFAECF00EF68ED /* CCGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGL.h; sourceTree = ""; }; 50643BD719BFAF4400EF68ED /* CCApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCApplication.h; sourceTree = ""; }; 50643BD819BFAF4400EF68ED /* CCStdC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCStdC.h; sourceTree = ""; }; @@ -5606,6 +5612,8 @@ 1A5700A2180BC5E60088DEC7 /* base */ = { isa = PBXGroup; children = ( + 505385001B01887A00793096 /* CCProperties.h */, + 505385011B01887A00793096 /* CCProperties.cpp */, B63990CA1A490AFE00B07923 /* CCAsyncTaskPool.cpp */, B63990CB1A490AFE00B07923 /* CCAsyncTaskPool.h */, D0FD03391A3B51AA00825BB5 /* allocator */, @@ -9245,6 +9253,7 @@ B6CAB5451AF9AA1A00B9B856 /* MiniCLTask.h in Headers */, 15AE1A2F19AAD3D500C27E9E /* b2TimeOfImpact.h in Headers */, 15AE18F319AAD35000C27E9E /* CCArmatureDataManager.h in Headers */, + 505385021B01887A00793096 /* CCProperties.h in Headers */, B665E2C01AA80A6500DDB1C5 /* CCPUGeometryRotator.h in Headers */, 50ABBE471925AB6F00A911A9 /* CCEvent.h in Headers */, 5012169C1AC473A3009A4BEA /* CCTechnique.h in Headers */, @@ -10117,6 +10126,7 @@ B6CAB3FA1AF9AA1A00B9B856 /* btMultiBody.h in Headers */, 15AE1BA619AADFDF00C27E9E /* UIHBox.h in Headers */, B6CAB2881AF9AA1A00B9B856 /* btBvhTriangleMeshShape.h in Headers */, + 505385031B01887A00793096 /* CCProperties.h in Headers */, 15AE1A9519AAD40300C27E9E /* b2BlockAllocator.h in Headers */, 5034CA48191D591100CE6051 /* ccShader_Label_normal.frag in Headers */, B6CAB30A1AF9AA1A00B9B856 /* btTriangleMesh.h in Headers */, @@ -10635,6 +10645,7 @@ B665E2761AA80A6500DDB1C5 /* CCPUDoPlacementParticleEventHandlerTranslator.cpp in Sources */, 1A570081180BC5A10088DEC7 /* CCActionManager.cpp in Sources */, 15AE1A6119AAD40300C27E9E /* b2Fixture.cpp in Sources */, + 505385041B01887A00793096 /* CCProperties.cpp in Sources */, 1A570085180BC5A10088DEC7 /* CCActionPageTurn3D.cpp in Sources */, B6CAB2B71AF9AA1A00B9B856 /* btConvexTriangleMeshShape.cpp in Sources */, 382384441A25915C002C4610 /* SpriteReader.cpp in Sources */, @@ -11655,6 +11666,7 @@ 382384141A259092002C4610 /* NodeReaderProtocol.cpp in Sources */, 50ABBD511925AB0000A911A9 /* Quaternion.cpp in Sources */, 1A5701FC180BCBAD0088DEC7 /* CCMenuItem.cpp in Sources */, + 505385051B01887A00793096 /* CCProperties.cpp in Sources */, B6CAB4201AF9AA1A00B9B856 /* btMLCPSolver.cpp in Sources */, B665E2A71AA80A6500DDB1C5 /* CCPUEventHandlerTranslator.cpp in Sources */, B6CAB4341AF9AA1A00B9B856 /* btGpu3DGridBroadphase.cpp in Sources */, diff --git a/cocos/2d/CCParticleSystem.cpp b/cocos/2d/CCParticleSystem.cpp index afed7bfaa4..4cbd7f6486 100644 --- a/cocos/2d/CCParticleSystem.cpp +++ b/cocos/2d/CCParticleSystem.cpp @@ -164,7 +164,7 @@ bool ParticleSystem::initWithFile(const std::string& plistFile) { bool ret = false; _plistFile = FileUtils::getInstance()->fullPathForFilename(plistFile); - ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(_plistFile.c_str()); + ValueMap dict = FileUtils::getInstance()->getValueMapFromFile(_plistFile); CCASSERT( !dict.empty(), "Particles: file not found"); diff --git a/cocos/2d/libcocos2d.vcxproj b/cocos/2d/libcocos2d.vcxproj index 5b4f2c5f6d..327ed303a4 100644 --- a/cocos/2d/libcocos2d.vcxproj +++ b/cocos/2d/libcocos2d.vcxproj @@ -430,6 +430,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\chipmunk\prebuilt\win32\release-lib\*.* + @@ -991,6 +992,7 @@ xcopy /Y /Q "$(ProjectDir)..\..\external\chipmunk\prebuilt\win32\release-lib\*.* + diff --git a/cocos/2d/libcocos2d.vcxproj.filters b/cocos/2d/libcocos2d.vcxproj.filters index 82368bef47..7c0be83e1c 100644 --- a/cocos/2d/libcocos2d.vcxproj.filters +++ b/cocos/2d/libcocos2d.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -1866,6 +1866,9 @@ renderer + + base + @@ -3650,6 +3653,9 @@ renderer + + base + diff --git a/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems b/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems index 69cbaab259..6de3d157fc 100644 --- a/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems +++ b/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems @@ -284,6 +284,7 @@ + @@ -883,6 +884,7 @@ + diff --git a/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems.filters b/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems.filters index b2c30b3426..bd8e7db20e 100644 --- a/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems.filters +++ b/cocos/2d/libcocos2d_8_1/libcocos2d_8_1/libcocos2d_8_1.Shared/libcocos2d_8_1.Shared.vcxitems.filters @@ -1785,6 +1785,9 @@ renderer + + base + @@ -3393,6 +3396,9 @@ renderer + + base + diff --git a/cocos/3d/CCMesh.cpp b/cocos/3d/CCMesh.cpp index d75fcf85a9..598a38ac10 100644 --- a/cocos/3d/CCMesh.cpp +++ b/cocos/3d/CCMesh.cpp @@ -570,7 +570,7 @@ void Mesh::setLightUniforms(GLProgramState* glProgramState, Scene* scene, const glProgramState->setUniformFloatv(s_spotLightUniformRangeInverseName, &s_spotLightUniformRangeInverseValues[0], s_spotLightUniformRangeInverseValues.size()); } - glProgramState->setUniformVec3(s_ambientLightUniformColorName, Vec3(ambientColor.x, ambientColor.y, ambientColor.z)); + glProgramState->setUniformVec3(s_ambientLightUniformColorName, Vec3(ambientColor.x, ambientColor.y, ambientColor.z)); } else // normal does not exist { diff --git a/cocos/3d/CCSprite3D.cpp b/cocos/3d/CCSprite3D.cpp index 91531a147b..783c4e7f6b 100644 --- a/cocos/3d/CCSprite3D.cpp +++ b/cocos/3d/CCSprite3D.cpp @@ -49,7 +49,20 @@ NS_CC_BEGIN -std::string s_attributeNames[] = {GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::ATTRIBUTE_NAME_TEX_COORD2,GLProgram::ATTRIBUTE_NAME_TEX_COORD3,GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT, GLProgram::ATTRIBUTE_NAME_BLEND_INDEX}; +static GLProgramState* getGLProgramStateForAttribs(MeshVertexData* meshVertexData, bool usesLight); +static void setVertexAttribForGLProgramState(GLProgramState* glprogramstate, const MeshVertexData* meshVertexData); + +std::string s_attributeNames[] = { + GLProgram::ATTRIBUTE_NAME_POSITION, + GLProgram::ATTRIBUTE_NAME_COLOR, + GLProgram::ATTRIBUTE_NAME_TEX_COORD, + GLProgram::ATTRIBUTE_NAME_TEX_COORD1, + GLProgram::ATTRIBUTE_NAME_TEX_COORD2, + GLProgram::ATTRIBUTE_NAME_TEX_COORD3, + GLProgram::ATTRIBUTE_NAME_NORMAL, + GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT, + GLProgram::ATTRIBUTE_NAME_BLEND_INDEX +}; Sprite3D* Sprite3D::create() { @@ -64,7 +77,7 @@ Sprite3D* Sprite3D::create() return nullptr; } -Sprite3D* Sprite3D::create(const std::string &modelPath) +Sprite3D* Sprite3D::create(const std::string& modelPath) { CCASSERT(modelPath.length() >= 4, "invalid filename for Sprite3D"); @@ -78,7 +91,7 @@ Sprite3D* Sprite3D::create(const std::string &modelPath) CC_SAFE_DELETE(sprite); return nullptr; } -Sprite3D* Sprite3D::create(const std::string &modelPath, const std::string &texturePath) +Sprite3D* Sprite3D::create(const std::string& modelPath, const std::string& texturePath) { auto sprite = create(modelPath); if (sprite) @@ -89,12 +102,12 @@ Sprite3D* Sprite3D::create(const std::string &modelPath, const std::string &text return sprite; } -void Sprite3D::createAsync(const std::string &modelPath, const std::function& callback, void* callbackparam) +void Sprite3D::createAsync(const std::string& modelPath, const std::function& callback, void* callbackparam) { createAsync(modelPath, "", callback, callbackparam); } -void Sprite3D::createAsync(const std::string &modelPath, const std::string &texturePath, const std::function& callback, void* callbackparam) +void Sprite3D::createAsync(const std::string& modelPath, const std::string& texturePath, const std::function& callback, void* callbackparam) { Sprite3D *sprite = new (std::nothrow) Sprite3D(); if (sprite->loadFromCache(modelPath)) @@ -249,6 +262,7 @@ Sprite3D::Sprite3D() , _lightMask(-1) , _shaderUsingLight(false) , _forceDepthWrite(false) +, _usingAutogeneratedGLProgram(true) { } @@ -269,7 +283,7 @@ bool Sprite3D::init() return false; } -bool Sprite3D::initWithFile(const std::string &path) +bool Sprite3D::initWithFile(const std::string& path) { _meshes.clear(); _meshVertexDatas.clear(); @@ -365,7 +379,7 @@ Sprite3D* Sprite3D::createSprite3DNode(NodeData* nodedata,ModelData* modeldata,c auto tex = Director::getInstance()->getTextureCache()->addImage(textureData->filename); if(tex) { - Texture2D::TexParams texParams; + Texture2D::TexParams texParams; texParams.minFilter = GL_LINEAR; texParams.magFilter = GL_LINEAR; texParams.wrapS = textureData->wrapS; @@ -396,7 +410,7 @@ Sprite3D* Sprite3D::createSprite3DNode(NodeData* nodedata,ModelData* modeldata,c } return sprite; } -void Sprite3D::createAttachSprite3DNode(NodeData* nodedata,const MaterialDatas& materialdatas) +void Sprite3D::createAttachSprite3DNode(NodeData* nodedata, const MaterialDatas& materialdatas) { for(const auto& it : nodedata->modelNodeDatas) { @@ -425,76 +439,68 @@ void Sprite3D::setMaterial(Material *material, int meshIndex) CCASSERT(material, "Invalid Material"); CCASSERT(meshIndex == -1 || (meshIndex >=0 && meshIndex < _meshes.size()), "Invalid meshIndex"); + if (meshIndex == -1) { - for(auto& mesh: _meshes) + for (auto mesh: _meshes) { mesh->setMaterial(material); + + // set the vertex attribs + for (auto technique: material->getTechniques()) + { + for (auto pass: technique->getPasses()) + { + auto meshVertexData = mesh->getMeshIndexData()->getMeshVertexData(); + auto glProgramState = pass->getGLProgramState(); + setVertexAttribForGLProgramState(glProgramState, meshVertexData); + } + } + } } else { - _meshes.at(meshIndex)->setMaterial(material); + auto mesh = _meshes.at(meshIndex); + mesh->setMaterial(material); + + // set the vertex attribs + for (auto technique: material->getTechniques()) + { + for (auto pass: technique->getPasses()) + { + auto meshVertexData = mesh->getMeshIndexData()->getMeshVertexData(); + auto glProgramState = pass->getGLProgramState(); + setVertexAttribForGLProgramState(glProgramState, meshVertexData); + } + } } + + _usingAutogeneratedGLProgram = false; } +Material* Sprite3D::getMaterial(int meshIndex) const +{ + CCASSERT(meshIndex >=0 && meshIndex < _meshes.size(), "Invalid meshIndex"); + return _meshes.at(meshIndex)->getMaterial(); +} + + void Sprite3D::genGLProgramState(bool useLight) { _shaderUsingLight = useLight; - + std::unordered_map glProgramestates; - for(auto& meshVertexData : _meshVertexDatas) + for(auto meshVertexData : _meshVertexDatas) { - bool textured = meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_TEX_COORD); - bool hasSkin = meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_BLEND_INDEX) - && meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_BLEND_WEIGHT); - bool hasNormal = meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_NORMAL); + auto glprogramstate = getGLProgramStateForAttribs(meshVertexData, useLight); + setVertexAttribForGLProgramState(glprogramstate, meshVertexData); - const char* shader = nullptr; - if(textured) - { - if (hasSkin) - { - if (hasNormal && _shaderUsingLight) - shader = GLProgram::SHADER_3D_SKINPOSITION_NORMAL_TEXTURE; - else - shader = GLProgram::SHADER_3D_SKINPOSITION_TEXTURE; - } - else - { - if (hasNormal && _shaderUsingLight) - shader = GLProgram::SHADER_3D_POSITION_NORMAL_TEXTURE; - else - shader = GLProgram::SHADER_3D_POSITION_TEXTURE; - } - } - else - { - shader = GLProgram::SHADER_3D_POSITION; - } - - CCASSERT(shader, "Couldn't find shader for sprite"); - - auto glProgram = GLProgramCache::getInstance()->getGLProgram(shader); - auto glprogramstate = GLProgramState::create(glProgram); - - long offset = 0; - auto attributeCount = meshVertexData->getMeshVertexAttribCount(); - for (auto k = 0; k < attributeCount; k++) { - auto meshattribute = meshVertexData->getMeshVertexAttrib(k); - glprogramstate->setVertexAttribPointer(s_attributeNames[meshattribute.vertexAttrib], - meshattribute.size, - meshattribute.type, - GL_FALSE, - meshVertexData->getVertexBuffer()->getSizePerVertex(), - (GLvoid*)offset); - offset += meshattribute.attribSizeBytes; - } - glProgramestates[meshVertexData] = glprogramstate; } - for (auto& mesh : _meshes) { + for (auto& mesh: _meshes) + { auto glProgramState = glProgramestates[mesh->getMeshIndexData()->getMeshVertexData()]; // hack to prevent cloning the very first time @@ -745,7 +751,9 @@ void Sprite3D::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) //check light and determine the shader used const auto& scene = Director::getInstance()->getRunningScene(); - if (scene) + + // Don't override GLProgramState if using manually set Material + if (_usingAutogeneratedGLProgram && scene) { const auto& lights = scene->getLights(); bool usingLight = false; @@ -755,7 +763,9 @@ void Sprite3D::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) break; } if (usingLight != _shaderUsingLight) + { genGLProgramState(usingLight); + } } for (auto& mesh : _meshes) @@ -973,4 +983,62 @@ Sprite3DCache::~Sprite3DCache() removeAllSprite3DData(); } +// +// MARK: Helpers +// +static GLProgramState* getGLProgramStateForAttribs(MeshVertexData* meshVertexData, bool usesLight) +{ + bool textured = meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_TEX_COORD); + bool hasSkin = meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_BLEND_INDEX) + && meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_BLEND_WEIGHT); + bool hasNormal = meshVertexData->hasVertexAttrib(GLProgram::VERTEX_ATTRIB_NORMAL); + + const char* shader = nullptr; + if(textured) + { + if (hasSkin) + { + if (hasNormal && usesLight) + shader = GLProgram::SHADER_3D_SKINPOSITION_NORMAL_TEXTURE; + else + shader = GLProgram::SHADER_3D_SKINPOSITION_TEXTURE; + } + else + { + if (hasNormal && usesLight) + shader = GLProgram::SHADER_3D_POSITION_NORMAL_TEXTURE; + else + shader = GLProgram::SHADER_3D_POSITION_TEXTURE; + } + } + else + { + shader = GLProgram::SHADER_3D_POSITION; + } + + CCASSERT(shader, "Couldn't find shader for sprite"); + + auto glProgram = GLProgramCache::getInstance()->getGLProgram(shader); + auto glprogramstate = GLProgramState::create(glProgram); + + return glprogramstate; +} + +static void setVertexAttribForGLProgramState(GLProgramState* glprogramstate, const MeshVertexData* meshVertexData) +{ + long offset = 0; + auto attributeCount = meshVertexData->getMeshVertexAttribCount(); + for (auto k = 0; k < attributeCount; k++) + { + auto meshattribute = meshVertexData->getMeshVertexAttrib(k); + glprogramstate->setVertexAttribPointer( + s_attributeNames[meshattribute.vertexAttrib], + meshattribute.size, + meshattribute.type, + GL_FALSE, + meshVertexData->getVertexBuffer()->getSizePerVertex(), + (GLvoid*)offset); + offset += meshattribute.attribSizeBytes; + } +} NS_CC_END diff --git a/cocos/3d/CCSprite3D.h b/cocos/3d/CCSprite3D.h index 586e0cfff3..5aad363f70 100644 --- a/cocos/3d/CCSprite3D.h +++ b/cocos/3d/CCSprite3D.h @@ -189,6 +189,12 @@ public: */ void setMaterial(Material* material, int meshIndex); + /** Adds a new material to a particular mesh of the sprite. + meshIndex is the mesh that will be applied to. + if meshIndex == -1, then it will be applied to all the meshes that belong to the sprite. + */ + Material* getMaterial(int meshIndex) const; + CC_CONSTRUCTOR_ACCESS: Sprite3D(); @@ -246,6 +252,7 @@ protected: unsigned int _lightMask; bool _shaderUsingLight; // is current shader using light ? bool _forceDepthWrite; // Always write to depth buffer + bool _usingAutogeneratedGLProgram; struct AsyncLoadParam { diff --git a/cocos/Android.mk b/cocos/Android.mk index ce6a61eba0..f69f5b8e68 100644 --- a/cocos/Android.mk +++ b/cocos/Android.mk @@ -132,6 +132,7 @@ base/CCEventTouch.cpp \ base/CCIMEDispatcher.cpp \ base/CCNS.cpp \ base/CCProfiling.cpp \ +base/CCProperties.cpp \ base/CCRef.cpp \ base/CCScheduler.cpp \ base/CCScriptSupport.cpp \ diff --git a/cocos/base/CCProperties.cpp b/cocos/base/CCProperties.cpp new file mode 100644 index 0000000000..56c5fc79af --- /dev/null +++ b/cocos/base/CCProperties.cpp @@ -0,0 +1,1357 @@ +/** + Copyright 2013 BlackBerry Inc. + Copyright (c) 2015 Chukong Technologies + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Original file from GamePlay3D: http://gameplay3d.org + + This file was modified to fit the cocos2d-x project + */ + + +#include "CCProperties.h" +#include "platform/CCPlatformMacros.h" +#include "platform/CCFileUtils.h" +#include "math/Vec2.h" +#include "math/Vec3.h" +#include "math/Vec4.h" +#include "math/Mat4.h" +#include "base/ccUTF8.h" +#include "base/CCData.h" +#include "deprecated/CCString.h" + + + +USING_NS_CC; + +static void printProperties(Properties* properties) +{ + // Print the name and ID of the current namespace. + const char* spacename = properties->getNamespace(); + const char* id = properties->getId(); + CCLOG("Namespace: %s ID: %s\n{", spacename, id); + + // Print all properties in this namespace. + const char* name = properties->getNextProperty(); + const char* value = NULL; + while (name != NULL) + { + value = properties->getString(name); + CCLOG("%s = %s", name, value); + name = properties->getNextProperty(); + } + + // Print the properties of every namespace within this one. + Properties* space = properties->getNextNamespace(); + while (space != NULL) + { + printProperties(space); + space = properties->getNextNamespace(); + } + + CCLOG("}\n"); +} + +// Utility functions (shared with SceneLoader). +/** @script{ignore} */ +void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector& namespacePath); +/** @script{ignore} */ +Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector& namespacePath); + +Properties::Properties() +: _variables(nullptr), _dirPath(nullptr), _parent(nullptr), _dataIdx(nullptr), _data(nullptr) +{ +} + +Properties::Properties(const Properties& copy) + : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties), + _variables(nullptr), _dirPath(nullptr), _parent(copy._parent), + _dataIdx(copy._dataIdx), _data(copy._data) +{ + setDirectoryPath(copy._dirPath); + _namespaces = std::vector(); + std::vector::const_iterator it; + for (it = copy._namespaces.begin(); it < copy._namespaces.end(); ++it) + { + GP_ASSERT(*it); + _namespaces.push_back(new (std::nothrow) Properties(**it)); + } + rewind(); +} + +Properties::Properties(Data* data, ssize_t* dataIdx) + : _variables(NULL), _dirPath(NULL), _parent(NULL), _dataIdx(dataIdx), _data(data) +{ + readProperties(); + rewind(); +} + +Properties::Properties(Data* data, ssize_t* dataIdx, const std::string& name, const char* id, const char* parentID, Properties* parent) + : _namespace(name), _variables(NULL), _dirPath(NULL), _parent(parent), _dataIdx(dataIdx), _data(data) +{ + if (id) + { + _id = id; + } + if (parentID) + { + _parentID = parentID; + } + readProperties(); + rewind(); +} + +Properties* Properties::create(const std::string& url) +{ + if (url.size() == 0) + { + CCLOGERROR("Attempting to create a Properties object from an empty URL!"); + return NULL; + } + + // Calculate the file and full namespace path from the specified url. + std::string urlString = url; + std::string fileString; + std::vector namespacePath; + calculateNamespacePath(urlString, fileString, namespacePath); + + + // data will be released automatically when 'data' goes out of scope + // so we pass data as weak pointer + auto data = FileUtils::getInstance()->getDataFromFile(fileString); + ssize_t dataIdx = 0; + Properties* properties = new (std::nothrow) Properties(&data, &dataIdx); + properties->resolveInheritance(); + + // Get the specified properties object. + Properties* p = getPropertiesFromNamespacePath(properties, namespacePath); + if (!p) + { + CCLOGWARN("Failed to load properties from url '%s'.", url.c_str()); + CC_SAFE_DELETE(properties); + return NULL; + } + + // If the loaded properties object is not the root namespace, + // then we have to clone it and delete the root namespace + // so that we don't leak memory. + if (p != properties) + { + p = p->clone(); + CC_SAFE_DELETE(properties); + } + // XXX +// p->setDirectoryPath(FileSystem::getDirectoryName(fileString)); + p->setDirectoryPath(""); + return p; +} + +static bool isVariable(const char* str, char* outName, size_t outSize) +{ + size_t len = strlen(str); + if (len > 3 && str[0] == '$' && str[1] == '{' && str[len - 1] == '}') + { + size_t size = len - 3; + if (size > (outSize - 1)) + size = outSize - 1; + strncpy(outName, str + 2, len - 3); + outName[len - 3] = 0; + return true; + } + + return false; +} + +void Properties::readProperties() +{ + CCASSERT(_data->getSize() >0, "Invalid data"); + + char line[2048]; + char variable[256]; + int c; + char* name; + char* value; + char* parentID; + char* rc; + char* rcc; + char* rccc; + bool comment = false; + + while (true) + { + // Skip whitespace at the start of lines + skipWhiteSpace(); + + // Stop when we have reached the end of the file. + if (eof()) + break; + + // Read the next line. + rc = readLine(line, 2048); + if (rc == NULL) + { + CCLOGERROR("Error reading line from file."); + return; + } + + // Ignore comments + if (comment) + { + // Check for end of multi-line comment at either start or end of line + if (strncmp(line, "*/", 2) == 0) + comment = false; + else + { + trimWhiteSpace(line); + const int len = strlen(line); + if (len >= 2 && strncmp(line + (len - 2), "*/", 2) == 0) + comment = false; + } + } + else if (strncmp(line, "/*", 2) == 0) + { + // Start of multi-line comment (must be at start of line) + comment = true; + } + else if (strncmp(line, "//", 2) != 0) + { + // If an '=' appears on this line, parse it as a name/value pair. + // Note: strchr() has to be called before strtok(), or a backup of line has to be kept. + rc = strchr(line, '='); + if (rc != NULL) + { + // First token should be the property name. + name = strtok(line, "="); + if (name == NULL) + { + CCLOGERROR("Error parsing properties file: attribute without name."); + return; + } + + // Remove white-space from name. + name = trimWhiteSpace(name); + + // Scan for next token, the property's value. + value = strtok(NULL, ""); + if (value == NULL) + { + CCLOGERROR("Error parsing properties file: attribute with name ('%s') but no value.", name); + return; + } + + // Remove white-space from value. + value = trimWhiteSpace(value); + + // Is this a variable assignment? + if (isVariable(name, variable, 256)) + { + setVariable(variable, value); + } + else + { + // Normal name/value pair + _properties.push_back(Property(name, value)); + } + } + else + { + parentID = NULL; + + // Get the last character on the line (ignoring whitespace). + const char* lineEnd = trimWhiteSpace(line) + (strlen(trimWhiteSpace(line)) - 1); + + // This line might begin or end a namespace, + // or it might be a key/value pair without '='. + + // Check for '{' on same line. + rc = strchr(line, '{'); + + // Check for inheritance: ':' + rcc = strchr(line, ':'); + + // Check for '}' on same line. + rccc = strchr(line, '}'); + + // Get the name of the namespace. + name = strtok(line, " \t\n{"); + name = trimWhiteSpace(name); + if (name == NULL) + { + CCLOGERROR("Error parsing properties file: failed to determine a valid token for line '%s'.", line); + return; + } + else if (name[0] == '}') + { + // End of namespace. + return; + } + + // Get its ID if it has one. + value = strtok(NULL, ":{"); + value = trimWhiteSpace(value); + + // Get its parent ID if it has one. + if (rcc != NULL) + { + parentID = strtok(NULL, "{"); + parentID = trimWhiteSpace(parentID); + } + + if (value != NULL && value[0] == '{') + { + // If the namespace ends on this line, seek back to right before the '}' character. + if (rccc && rccc == lineEnd) + { + if (seekFromCurrent(-1) == false) + { + CCLOGERROR("Failed to seek back to before a '}' character in properties file."); + return; + } + while (readChar() != '}') + { + if (seekFromCurrent(-2) == false) + { + CCLOGERROR("Failed to seek back to before a '}' character in properties file."); + return; + } + } + if (seekFromCurrent(-1) == false) + { + CCLOGERROR("Failed to seek back to before a '}' character in properties file."); + return; + } + } + + // New namespace without an ID. + Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, NULL, parentID, this); + _namespaces.push_back(space); + + // If the namespace ends on this line, seek to right after the '}' character. + if (rccc && rccc == lineEnd) + { + if (seekFromCurrent(1) == false) + { + CCLOGERROR("Failed to seek to immediately after a '}' character in properties file."); + return; + } + } + } + else + { + // If '{' appears on the same line. + if (rc != NULL) + { + // If the namespace ends on this line, seek back to right before the '}' character. + if (rccc && rccc == lineEnd) + { + if (seekFromCurrent(-1) == false) + { + CCLOGERROR("Failed to seek back to before a '}' character in properties file."); + return; + } + while (readChar() != '}') + { + if (seekFromCurrent(-2) == false) + { + CCLOGERROR("Failed to seek back to before a '}' character in properties file."); + return; + } + } + if (seekFromCurrent(-1) == false) + { + CCLOGERROR("Failed to seek back to before a '}' character in properties file."); + return; + } + } + + // Create new namespace. + Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, value, parentID, this); + _namespaces.push_back(space); + + // If the namespace ends on this line, seek to right after the '}' character. + if (rccc && rccc == lineEnd) + { + if (seekFromCurrent(1) == false) + { + CCLOGERROR("Failed to seek to immediately after a '}' character in properties file."); + return; + } + } + } + else + { + // Find out if the next line starts with "{" + skipWhiteSpace(); + c = readChar(); + if (c == '{') + { + // Create new namespace. + Properties* space = new (std::nothrow) Properties(_data, _dataIdx, name, value, parentID, this); + _namespaces.push_back(space); + } + else + { + // Back up from fgetc() + if (seekFromCurrent(-1) == false) + CCLOGERROR("Failed to seek backwards a single character after testing if the next line starts with '{'."); + + // Store "name value" as a name/value pair, or even just "name". + if (value != NULL) + { + _properties.push_back(Property(name, value)); + } + else + { + _properties.push_back(Property(name, "")); + } + } + } + } + } + } + } +} + +Properties::~Properties() +{ + CC_SAFE_DELETE(_dirPath); + for (size_t i = 0, count = _namespaces.size(); i < count; ++i) + { + CC_SAFE_DELETE(_namespaces[i]); + } + + CC_SAFE_DELETE(_variables); +} + +// +// Stream simulation +// +signed char Properties::readChar() +{ + if (eof()) + return EOF; + return _data->getBytes()[(*_dataIdx)++]; +} + +char* Properties::readLine(char* output, int num) +{ + int idx=0; + + if (eof()) + return nullptr; + + auto c = readChar(); + while (c!=EOF && c!='\n' && idx-1= 0); +} + +bool Properties::eof() +{ + return (*_dataIdx >= _data->getSize()); +} + +void Properties::skipWhiteSpace() +{ + signed char c; + do + { + c = readChar(); + } while (isspace(c) && c != EOF); + + // If we are not at the end of the file, then since we found a + // non-whitespace character, we put the cursor back in front of it. + if (c != EOF) + { + if (seekFromCurrent(-1) == false) + { + CCLOGERROR("Failed to seek backwards one character after skipping whitespace."); + } + } +} + +char* Properties::trimWhiteSpace(char *str) +{ + if (str == NULL) + { + return str; + } + + char *end; + + // Trim leading space. + while (isspace(*str)) + str++; + + // All spaces? + if (*str == 0) + { + return str; + } + + // Trim trailing space. + end = str + strlen(str) - 1; + while (end > str && isspace(*end)) + end--; + + // Write new null terminator. + *(end+1) = 0; + + return str; +} + +void Properties::resolveInheritance(const char* id) +{ + // Namespaces can be defined like so: + // "name id : parentID { }" + // This method merges data from the parent namespace into the child. + + // Get a top-level namespace. + Properties* derived; + if (id) + { + derived = getNamespace(id); + } + else + { + derived = getNextNamespace(); + } + while (derived) + { + // If the namespace has a parent ID, find the parent. + if (!derived->_parentID.empty()) + { + Properties* parent = getNamespace(derived->_parentID.c_str()); + if (parent) + { + resolveInheritance(parent->getId()); + + // Copy the child. + Properties* overrides = new (std::nothrow) Properties(*derived); + + // Delete the child's data. + for (size_t i = 0, count = derived->_namespaces.size(); i < count; i++) + { + CC_SAFE_DELETE(derived->_namespaces[i]); + } + + // Copy data from the parent into the child. + derived->_properties = parent->_properties; + derived->_namespaces = std::vector(); + std::vector::const_iterator itt; + for (itt = parent->_namespaces.begin(); itt < parent->_namespaces.end(); ++itt) + { + GP_ASSERT(*itt); + derived->_namespaces.push_back(new (std::nothrow) Properties(**itt)); + } + derived->rewind(); + + // Take the original copy of the child and override the data copied from the parent. + derived->mergeWith(overrides); + + // Delete the child copy. + CC_SAFE_DELETE(overrides); + } + } + + // Resolve inheritance within this namespace. + derived->resolveInheritance(); + + // Get the next top-level namespace and check again. + if (!id) + { + derived = getNextNamespace(); + } + else + { + derived = NULL; + } + } +} + +void Properties::mergeWith(Properties* overrides) +{ + GP_ASSERT(overrides); + + // Overwrite or add each property found in child. + overrides->rewind(); + const char* name = overrides->getNextProperty(); + while (name) + { + this->setString(name, overrides->getString()); + name = overrides->getNextProperty(); + } + this->_propertiesItr = this->_properties.end(); + + // Merge all common nested namespaces, add new ones. + Properties* overridesNamespace = overrides->getNextNamespace(); + while (overridesNamespace) + { + bool merged = false; + + rewind(); + Properties* derivedNamespace = getNextNamespace(); + while (derivedNamespace) + { + if (strcmp(derivedNamespace->getNamespace(), overridesNamespace->getNamespace()) == 0 && + strcmp(derivedNamespace->getId(), overridesNamespace->getId()) == 0) + { + derivedNamespace->mergeWith(overridesNamespace); + merged = true; + } + + derivedNamespace = getNextNamespace(); + } + + if (!merged) + { + // Add this new namespace. + Properties* newNamespace = new (std::nothrow) Properties(*overridesNamespace); + + this->_namespaces.push_back(newNamespace); + this->_namespacesItr = this->_namespaces.end(); + } + + overridesNamespace = overrides->getNextNamespace(); + } +} + +const char* Properties::getNextProperty() +{ + if (_propertiesItr == _properties.end()) + { + // Restart from the beginning + _propertiesItr = _properties.begin(); + } + else + { + // Move to the next property + ++_propertiesItr; + } + + return _propertiesItr == _properties.end() ? NULL : _propertiesItr->name.c_str(); +} + +Properties* Properties::getNextNamespace() +{ + if (_namespacesItr == _namespaces.end()) + { + // Restart from the beginning + _namespacesItr = _namespaces.begin(); + } + else + { + ++_namespacesItr; + } + + if (_namespacesItr != _namespaces.end()) + { + Properties* ns = *_namespacesItr; + return ns; + } + + return NULL; +} + +void Properties::rewind() +{ + _propertiesItr = _properties.end(); + _namespacesItr = _namespaces.end(); +} + +Properties* Properties::getNamespace(const char* id, bool searchNames, bool recurse) const +{ + GP_ASSERT(id); + + for (std::vector::const_iterator it = _namespaces.begin(); it < _namespaces.end(); ++it) + { + Properties* p = *it; + if (strcmp(searchNames ? p->_namespace.c_str() : p->_id.c_str(), id) == 0) + return p; + + if (recurse) + { + // Search recursively. + p = p->getNamespace(id, searchNames, true); + if (p) + return p; + } + } + + return NULL; +} + +const char* Properties::getNamespace() const +{ + return _namespace.c_str(); +} + +const char* Properties::getId() const +{ + return _id.c_str(); +} + +bool Properties::exists(const char* name) const +{ + if (name == NULL) + return false; + + for (std::list::const_iterator itr = _properties.begin(); itr != _properties.end(); ++itr) + { + if (itr->name == name) + return true; + } + + return false; +} + +static const bool isStringNumeric(const char* str) +{ + GP_ASSERT(str); + + // The first character may be '-' + if (*str == '-') + str++; + + // The first character after the sign must be a digit + if (!isdigit(*str)) + return false; + str++; + + // All remaining characters must be digits, with a single decimal (.) permitted + unsigned int decimalCount = 0; + while (*str) + { + if (!isdigit(*str)) + { + if (*str == '.' && decimalCount == 0) + { + // Max of 1 decimal allowed + decimalCount++; + } + else + { + return false; + } + } + str++; + } + return true; +} + +Properties::Type Properties::getType(const char* name) const +{ + const char* value = getString(name); + if (!value) + { + return Properties::NONE; + } + + // Parse the value to determine the format + unsigned int commaCount = 0; + char* valuePtr = const_cast(value); + while ((valuePtr = strchr(valuePtr, ','))) + { + valuePtr++; + commaCount++; + } + + switch (commaCount) + { + case 0: + return isStringNumeric(value) ? Properties::NUMBER : Properties::STRING; + case 1: + return Properties::VECTOR2; + case 2: + return Properties::VECTOR3; + case 3: + return Properties::VECTOR4; + case 15: + return Properties::MATRIX; + default: + return Properties::STRING; + } +} + +const char* Properties::getString(const char* name, const char* defaultValue) const +{ + char variable[256]; + const char* value = NULL; + + if (name) + { + // If 'name' is a variable, return the variable value + if (isVariable(name, variable, 256)) + { + return getVariable(variable, defaultValue); + } + + for (std::list::const_iterator itr = _properties.begin(); itr != _properties.end(); ++itr) + { + if (itr->name == name) + { + value = itr->value.c_str(); + break; + } + } + } + else + { + // No name provided - get the value at the current iterator position + if (_propertiesItr != _properties.end()) + { + value = _propertiesItr->value.c_str(); + } + } + + if (value) + { + // If the value references a variable, return the variable value + if (isVariable(value, variable, 256)) + return getVariable(variable, defaultValue); + + return value; + } + + return defaultValue; +} + +bool Properties::setString(const char* name, const char* value) +{ + if (name) + { + for (std::list::iterator itr = _properties.begin(); itr != _properties.end(); ++itr) + { + if (itr->name == name) + { + // Update the first property that matches this name + itr->value = value ? value : ""; + return true; + } + } + + // There is no property with this name, so add one + _properties.push_back(Property(name, value ? value : "")); + } + else + { + // If there's a current property, set its value + if (_propertiesItr == _properties.end()) + return false; + + _propertiesItr->value = value ? value : ""; + } + + return true; +} + +bool Properties::getBool(const char* name, bool defaultValue) const +{ + const char* valueString = getString(name); + if (valueString) + { + return (strcmp(valueString, "true") == 0); + } + + return defaultValue; +} + +int Properties::getInt(const char* name) const +{ + const char* valueString = getString(name); + if (valueString) + { + int value; + int scanned; + scanned = sscanf(valueString, "%d", &value); + if (scanned != 1) + { + CCLOGERROR("Error attempting to parse property '%s' as an integer.", name); + return 0; + } + return value; + } + + return 0; +} + +float Properties::getFloat(const char* name) const +{ + const char* valueString = getString(name); + if (valueString) + { + float value; + int scanned; + scanned = sscanf(valueString, "%f", &value); + if (scanned != 1) + { + CCLOGERROR("Error attempting to parse property '%s' as a float.", name); + return 0.0f; + } + return value; + } + + return 0.0f; +} + +long Properties::getLong(const char* name) const +{ + const char* valueString = getString(name); + if (valueString) + { + long value; + int scanned; + scanned = sscanf(valueString, "%ld", &value); + if (scanned != 1) + { + CCLOGERROR("Error attempting to parse property '%s' as a long integer.", name); + return 0L; + } + return value; + } + + return 0L; +} + +bool Properties::getMat4(const char* name, Mat4* out) const +{ + GP_ASSERT(out); + + const char* valueString = getString(name); + if (valueString) + { + float m[16]; + int scanned; + scanned = sscanf(valueString, "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", + &m[0], &m[1], &m[2], &m[3], &m[4], &m[5], &m[6], &m[7], + &m[8], &m[9], &m[10], &m[11], &m[12], &m[13], &m[14], &m[15]); + + if (scanned != 16) + { + CCLOGERROR("Error attempting to parse property '%s' as a matrix.", name); + out->setIdentity(); + return false; + } + + out->set(m); + return true; + } + + out->setIdentity(); + return false; +} + +bool Properties::getVec2(const char* name, Vec2* out) const +{ + return parseVec2(getString(name), out); +} + +bool Properties::getVec3(const char* name, Vec3* out) const +{ + return parseVec3(getString(name), out); +} + +bool Properties::getVec4(const char* name, Vec4* out) const +{ + return parseVec4(getString(name), out); +} + +bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const +{ + return parseAxisAngle(getString(name), out); +} + +bool Properties::getColor(const char* name, Vec3* out) const +{ + return parseColor(getString(name), out); +} + +bool Properties::getColor(const char* name, Vec4* out) const +{ + return parseColor(getString(name), out); +} + +bool Properties::getPath(const char* name, std::string* path) const +{ + GP_ASSERT(name && path); + const char* valueString = getString(name); + if (valueString) + { + if (FileUtils::getInstance()->isFileExist(valueString)) + { + path->assign(valueString); + return true; + } + else + { + const Properties* prop = this; + while (prop != NULL) + { + // Search for the file path relative to the bundle file + const std::string* dirPath = prop->_dirPath; + if (dirPath != NULL && !dirPath->empty()) + { + std::string relativePath = *dirPath; + relativePath.append(valueString); + if (FileUtils::getInstance()->isFileExist(relativePath.c_str())) + { + path->assign(relativePath); + return true; + } + } + prop = prop->_parent; + } + } + } + return false; +} + +const char* Properties::getVariable(const char* name, const char* defaultValue) const +{ + if (name == NULL) + return defaultValue; + + // Search for variable in this Properties object + if (_variables) + { + for (size_t i = 0, count = _variables->size(); i < count; ++i) + { + Property& prop = (*_variables)[i]; + if (prop.name == name) + return prop.value.c_str(); + } + } + + // Search for variable in parent Properties + return _parent ? _parent->getVariable(name, defaultValue) : defaultValue; +} + +void Properties::setVariable(const char* name, const char* value) +{ + GP_ASSERT(name); + + Property* prop = NULL; + + // Search for variable in this Properties object and parents + Properties* current = const_cast(this); + while (current) + { + if (current->_variables) + { + for (size_t i = 0, count = current->_variables->size(); i < count; ++i) + { + Property* p = &(*current->_variables)[i]; + if (p->name == name) + { + prop = p; + break; + } + } + } + current = current->_parent; + } + + if (prop) + { + // Found an existing property, set it + prop->value = value ? value : ""; + } + else + { + // Add a new variable with this name + if (!_variables) + _variables = new (std::nothrow) std::vector(); + _variables->push_back(Property(name, value ? value : "")); + } +} + +Properties* Properties::clone() +{ + Properties* p = new (std::nothrow) Properties(); + + p->_namespace = _namespace; + p->_id = _id; + p->_parentID = _parentID; + p->_properties = _properties; + p->_propertiesItr = p->_properties.end(); + p->setDirectoryPath(_dirPath); + + for (size_t i = 0, count = _namespaces.size(); i < count; i++) + { + GP_ASSERT(_namespaces[i]); + Properties* child = _namespaces[i]->clone(); + p->_namespaces.push_back(child); + child->_parent = p; + } + p->_namespacesItr = p->_namespaces.end(); + + return p; +} + +void Properties::setDirectoryPath(const std::string* path) +{ + if (path) + { + setDirectoryPath(*path); + } + else + { + CC_SAFE_DELETE(_dirPath); + } +} + +void Properties::setDirectoryPath(const std::string& path) +{ + if (_dirPath == NULL) + { + _dirPath = new (std::nothrow) std::string(path); + } + else + { + _dirPath->assign(path); + } +} + +void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector& namespacePath) +{ + // If the url references a specific namespace within the file, + // calculate the full namespace path to the final namespace. + size_t loc = urlString.rfind("#"); + if (loc != std::string::npos) + { + fileString = urlString.substr(0, loc); + std::string namespacePathString = urlString.substr(loc + 1); + while ((loc = namespacePathString.find("/")) != std::string::npos) + { + namespacePath.push_back(namespacePathString.substr(0, loc)); + namespacePathString = namespacePathString.substr(loc + 1); + } + namespacePath.push_back(namespacePathString); + } + else + { + fileString = urlString; + } +} + +Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector& namespacePath) +{ + // If the url references a specific namespace within the file, + // return the specified namespace or notify the user if it cannot be found. + if (namespacePath.size() > 0) + { + size_t size = namespacePath.size(); + properties->rewind(); + Properties* iter = properties->getNextNamespace(); + for (size_t i = 0; i < size; ) + { + while (true) + { + if (iter == NULL) + { + CCLOGWARN("Failed to load properties object from url."); + return NULL; + } + + if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0) + { + if (i != size - 1) + { + properties = iter->getNextNamespace(); + iter = properties; + } + else + properties = iter; + + i++; + break; + } + + iter = properties->getNextNamespace(); + } + } + + return properties; + } + else + return properties; +} + +bool Properties::parseVec2(const char* str, Vec2* out) +{ + if (str) + { + float x, y; + if (sscanf(str, "%f,%f", &x, &y) == 2) + { + if (out) + out->set(x, y); + return true; + } + else + { + CCLOGWARN("Error attempting to parse property as a two-dimensional vector: %s", str); + } + } + + if (out) + out->set(0.0f, 0.0f); + return false; +} + +bool Properties::parseVec3(const char* str, Vec3* out) +{ + if (str) + { + float x, y, z; + if (sscanf(str, "%f,%f,%f", &x, &y, &z) == 3) + { + if (out) + out->set(x, y, z); + return true; + } + else + { + CCLOGWARN("Error attempting to parse property as a three-dimensional vector: %s", str); + } + } + + if (out) + out->set(0.0f, 0.0f, 0.0f); + return false; +} + +bool Properties::parseVec4(const char* str, Vec4* out) +{ + if (str) + { + float x, y, z, w; + if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &w) == 4) + { + if (out) + out->set(x, y, z, w); + return true; + } + else + { + CCLOGWARN("Error attempting to parse property as a four-dimensional vector: %s", str); + } + } + + if (out) + out->set(0.0f, 0.0f, 0.0f, 0.0f); + return false; +} + +bool Properties::parseAxisAngle(const char* str, Quaternion* out) +{ + if (str) + { + float x, y, z, theta; + if (sscanf(str, "%f,%f,%f,%f", &x, &y, &z, &theta) == 4) + { + if (out) + out->set(Vec3(x, y, z), MATH_DEG_TO_RAD(theta)); + return true; + } + else + { + CCLOGWARN("Error attempting to parse property as an axis-angle rotation: %s", str); + } + } + + if (out) + out->set(0.0f, 0.0f, 0.0f, 1.0f); + return false; +} + +bool Properties::parseColor(const char* str, Vec3* out) +{ + if (str) + { + if (strlen(str) == 7 && str[0] == '#') + { + // Read the string into an int as hex. + unsigned int color; + if (sscanf(str + 1, "%x", &color) == 1) + { + if (out) + out->set(Vec3::fromColor(color)); + return true; + } + else + { + // Invalid format + CCLOGWARN("Error attempting to parse property as an RGB color: %s", str); + } + } + else + { + // Not a color string. + CCLOGWARN("Error attempting to parse property as an RGB color (not specified as a color string): %s", str); + } + } + + if (out) + out->set(0.0f, 0.0f, 0.0f); + return false; +} + +bool Properties::parseColor(const char* str, Vec4* out) +{ + if (str) + { + if (strlen(str) == 9 && str[0] == '#') + { + // Read the string into an int as hex. + unsigned int color; + if (sscanf(str + 1, "%x", &color) == 1) + { + if (out) + out->set(Vec4::fromColor(color)); + return true; + } + else + { + // Invalid format + CCLOGWARN("Error attempting to parse property as an RGBA color: %s", str); + } + } + else + { + // Not a color string. + CCLOGWARN("Error attempting to parse property as an RGBA color (not specified as a color string): %s", str); + } + } + + if (out) + out->set(0.0f, 0.0f, 0.0f, 0.0f); + return false; +} + + + diff --git a/cocos/base/CCProperties.h b/cocos/base/CCProperties.h new file mode 100644 index 0000000000..398598a997 --- /dev/null +++ b/cocos/base/CCProperties.h @@ -0,0 +1,616 @@ +/** + Copyright 2013 BlackBerry Inc. + Copyright (c) 2015 Chukong Technologies + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Original file from GamePlay3D: http://gameplay3d.org + + This file was modified to fit the cocos2d-x project + */ + + +#ifndef __cocos2d_libs__CCProperties__ +#define __cocos2d_libs__CCProperties__ + +#include +#include +#include +#include + +#include "renderer/CCTexture2D.h" +#include "platform/CCPlatformMacros.h" +#include "base/CCRef.h" +#include "base/ccTypes.h" +#include "base/CCVector.h" + +NS_CC_BEGIN + +class Properties; +class Vec2; +class Vec3; +class Vec4; +class Mat4; +class Data; +class Data; + + +/** + * Defines a properties file for loading text files. + * + * A properties file has very simple syntax and can contain only namespaces and + * name/value pairs (the properties of a namespace). + * The file can have any file extension a user specifies. + * + * Here's an example of a simple + * file that uses all the available features of the markup language: + + @verbatim + // This is a comment. + + // This property is in the default namespace: + integerProperty = 5 + + // This line defines a namespace of type "mynamespace" without an ID: + mynamespace + { + // This namespace can be retrieved by searching for its ID, "spriteTexture", + // or by its name "texture": + texture spriteTexture + { + fileName = sprite.png + width = 64 + height = 64 + } + + // This property is in the "space" namespace: + booleanProperty = true + + // It's legal to have a name without a value if you leave out the '=' character: + foo + + // In fact, the '=' character is optional if you'd rather write: + bar 23 + + // But don't write this or you'll get an error: + // illegalProperty = + + // Or this: + // = 15 + + // Properties objects let you retrieve values as various types. + floatProperty = 3.333 + stringProperty = This is a string. + vector3Property = 1.0, 5.0, 3.55 + colorProperty = 1.0, 0.4, 0.0, 1.0 + } + @endverbatim + + * Retrieving information out of a file like this could be done in two ways. If the + * available namespaces and name/value pairs are known in advance they can be queried by ID or name. + * For example, if the namespace "spriteTexture" and its properties are required then they can + * be retrieved with a call to getNamespace() followed by calls to getString() and getInt(). + * A namespace is stored and retrieved as a Properties object. + * Reading the spriteTexture properties out of the file above in this way could be done with the following code: + + @verbatim + // Create the top-level Properties object. + Properties* properties = Properties::create("example.properties"); + // Retrieve the "spriteTexture" namespace. + Properties* spriteTexture = properties->getNamespace("spriteTexture"); + + // Get the values of known texture properties out of the namespace. + const char* fileName = spriteTexture->getString("fileName"); + int width = spriteTexture->getInt("width"); + int height = spriteTexture->getInt("height"); + + // Deleting the top-level Properties object will clean up all nested namespaces. + SAFE_DELETE(properties); + @endverbatim + + * On the other hand, if the structure of the file is not known in advance its + * namespaces and name/value pairs can be retrieved one by one using the getNextNamespace() + * and getNextProperty() methods. The following method prints the contents of any properties file + * to the console: + + @verbatim + void printProperties(Properties* properties) + { + // Print the name and ID of the current namespace. + const char* spacename = properties->getNamespace(); + const char* id = properties->getId(); + GP_WARN("Namespace: %s ID: %s\n{", spacename, id); + + // Print all properties in this namespace. + const char* name = properties->getNextProperty(); + const char* value = NULL; + while (name != NULL) + { + value = properties->getString(name); + GP_WARN("%s = %s", name, value); + name = properties->getNextProperty(); + } + GP_WARN("}\n"); + + // Print the properties of every namespace within this one. + Properties* space = properties->getNextNamespace(); + while (space != NULL) + { + printProperties(space); + space = properties->getNextNamespace(); + } + } + @endverbatim + + * Note that this method does not keep track of the namespace hierarchy, but could be + * modified to do so. Also note that nothing in a properties file indicates the type + * of a property. If the type is unknown, its string can be retrieved and interpreted + * as necessary. + */ +class CC_DLL Properties +{ + friend class Game; + +public: + + /** + * Data types supported by the properties class. + */ + enum Type + { + NONE, + STRING, + NUMBER, + VECTOR2, + VECTOR3, + VECTOR4, + MATRIX + }; + + /** + * Creates a Properties runtime settings from the specified URL, where the URL is of + * the format ".#//.../" + * (and "#//.../" is optional). + * + * @param url The URL to create the properties from. + * + * @return The created Properties or NULL if there was an error. + * @script{create} + */ + static Properties* create(const std::string& url); + + /** + * Destructor. + */ + ~Properties(); + + /** + * Get the name of the next property. + * + * If a valid next property is returned, the value of the property can be + * retrieved using any of the get methods in this class, passing NULL for + // the property name. + * + * @return The name of the next property, or NULL if there are no properties remaining. + */ + const char* getNextProperty(); + + /** + * Get the next namespace. + */ + Properties* getNextNamespace(); + + /** + * Rewind the getNextProperty() and getNextNamespace() iterators + * to the beginning of the file. + */ + void rewind(); + + /** + * Get a specific namespace by ID or name. This method will optionally + * perform a depth-first search on all namespaces and inner namespaces + * within this Property. + * + * @param id The ID or name of the namespace to find. + * @param searchNames If true, namespace names are used in the search, + * instead of namespace IDs. By default this parameter is false + * and namespace IDs are searched. + * @param recurse If true, perform a depth-first search, otherwise search + * only the immediate child namespaces. + * + * @return A properties object with the given ID or name. + */ + Properties* getNamespace(const char* id, bool searchNames = false, bool recurse = true) const; + + /** + * Get the name of this Property's namespace. + * + * @return The name of this Property's namespace. + */ + const char* getNamespace() const; + + /** + * Get the ID of this Property's namespace. The ID should be a unique identifier, + * but its uniqueness is not enforced. + * + * @return The ID of this Property's namespace. + */ + const char* getId() const; + + /** + * Check if a property with the given name is specified in this Properties object. + * + * @param name The name of the property to query. + * + * @return True if the property exists, false otherwise. + */ + bool exists(const char* name) const; + + /** + * Returns the type of a property. + * + * @param name The name of the property to interpret, or NULL to return the current property's type. + * + * @return The type of the property. + */ + Type getType(const char* name = NULL) const; + + /** + * Get the value of the given property as a string. This can always be retrieved, + * whatever the intended type of the property. + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param defaultValue The default value to return if the specified property does not exist. + * + * @return The value of the given property as a string, or the empty string if no property with that name exists. + */ + const char* getString(const char* name = NULL, const char* defaultValue = NULL) const; + + /** + * Sets the value of the property with the specified name. + * + * If there is no property in this namespace with the current name, + * one is added. Otherwise, the value of the first property with the + * specified name is updated. + * + * If name is NULL, the value current property (see getNextProperty) is + * set, unless there is no current property, in which case false + * is returned. + * + * @param name The name of the property to set. + * @param value The property value. + * + * @return True if the property was set, false otherwise. + */ + bool setString(const char* name, const char* value); + + /** + * Interpret the value of the given property as a boolean. + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param defaultValue the default value to return if the specified property does not exist within the properties file. + * + * @return true if the property exists and its value is "true", otherwise false. + */ + bool getBool(const char* name = NULL, bool defaultValue = false) const; + + /** + * Interpret the value of the given property as an integer. + * If the property does not exist, zero will be returned. + * If the property exists but could not be scanned, an error will be logged and zero will be returned. + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * + * @return The value of the given property interpreted as an integer. + * Zero if the property does not exist or could not be scanned. + */ + int getInt(const char* name = NULL) const; + + /** + * Interpret the value of the given property as a floating-point number. + * If the property does not exist, zero will be returned. + * If the property exists but could not be scanned, an error will be logged and zero will be returned. + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * + * @return The value of the given property interpreted as a float. + * Zero if the property does not exist or could not be scanned. + */ + float getFloat(const char* name = NULL) const; + + /** + * Interpret the value of the given property as a long integer. + * If the property does not exist, zero will be returned. + * If the property exists but could not be scanned, an error will be logged and zero will be returned. + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * + * @return The value of the given property interpreted as a long. + * Zero if the property does not exist or could not be scanned. + */ + long getLong(const char* name = NULL) const; + + /** + * Interpret the value of the given property as a Matrix. + * If the property does not exist, out will be set to the identity matrix. + * If the property exists but could not be scanned, an error will be logged and out will be set + * to the identity matrix. + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The matrix to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getMat4(const char* name, Mat4* out) const; + + /** + * Interpret the value of the given property as a Vector2. + * If the property does not exist, out will be set to Vector2(0.0f, 0.0f). + * If the property exists but could not be scanned, an error will be logged and out will be set + * to Vector2(0.0f, 0.0f). + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The vector to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getVec2(const char* name, Vec2* out) const; + + /** + * Interpret the value of the given property as a Vector3. + * If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f). + * If the property exists but could not be scanned, an error will be logged and out will be set + * to Vector3(0.0f, 0.0f, 0.0f). + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The vector to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getVec3(const char* name, Vec3* out) const; + + /** + * Interpret the value of the given property as a Vector4. + * If the property does not exist, out will be set to Vector4(0.0f, 0.0f, 0.0f, 0.0f). + * If the property exists but could not be scanned, an error will be logged and out will be set + * to Vector4(0.0f, 0.0f, 0.0f, 0.0f). + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The vector to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getVec4(const char* name, Vec4* out) const; + + /** + * Interpret the value of the given property as a Quaternion specified as an axis angle. + * If the property does not exist, out will be set to Quaternion(). + * If the property exists but could not be scanned, an error will be logged and out will be set + * to Quaternion(). + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The quaternion to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getQuaternionFromAxisAngle(const char* name, Quaternion* out) const; + + /** + * Interpret the value of the given property as an RGB color in hex and write this color to a Vector3. + * E.g. 0xff0000 represents red and sets the vector to (1, 0, 0). + * If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f). + * If the property exists but could not be scanned, an error will be logged and out will be set + * to Vector3(0.0f, 0.0f, 0.0f). + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The vector to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getColor(const char* name, Vec3* out) const; + + /** + * Interpret the value of the given property as an RGBA color in hex and write this color to a Vector4. + * E.g. 0xff0000ff represents opaque red and sets the vector to (1, 0, 0, 1). + * If the property does not exist, out will be set to Vector4(0.0f, 0.0f, 0.0f, 0.0f). + * If the property exists but could not be scanned, an error will be logged and out will be set + * to Vector4(0.0f, 0.0f, 0.0f, 0.0f). + * + * @param name The name of the property to interpret, or NULL to return the current property's value. + * @param out The vector to set to this property's interpreted value. + * + * @return True on success, false if the property does not exist or could not be scanned. + */ + bool getColor(const char* name, Vec4* out) const; + + /** + * Gets the file path for the given property if the file exists. + * + * This method will first search for the file relative to the working directory. + * If the file is not found then it will search relative to the directory the bundle file is in. + * + * @param name The name of the property. + * @param path The string to copy the path to if the file exists. + * + * @return True if the property exists and the file exists, false otherwise. + * + * @script{ignore} + */ + bool getPath(const char* name, std::string* path) const; + + /** + * Returns the value of a variable that is set in this Properties object. + * + * Variables take on the format ${name} and are inherited from parent Property objects. + * + * @param name Name of the variable to get. + * @param defaultValue Value to return if the variable is not found. + * + * @return The value of the specified variable, or defaultValue if not found. + */ + const char* getVariable(const char* name, const char* defaultValue = NULL) const; + + /** + * Sets the value of the specified variable. + * + * @param name Name of the variable to set. + * @param value The value to set. + */ + void setVariable(const char* name, const char* value); + + /** + * Attempts to parse the specified string as a Vector2 value. + * + * On error, false is returned and the output is set to all zero values. + * + * @param str The string to parse. + * @param out The value to populate if successful. + * + * @return True if a valid Vector2 was parsed, false otherwise. + */ + static bool parseVec2(const char* str, Vec2* out); + + /** + * Attempts to parse the specified string as a Vector3 value. + * + * On error, false is returned and the output is set to all zero values. + * + * @param str The string to parse. + * @param out The value to populate if successful. + * + * @return True if a valid Vector3 was parsed, false otherwise. + */ + static bool parseVec3(const char* str, Vec3* out); + + /** + * Attempts to parse the specified string as a Vector4 value. + * + * On error, false is returned and the output is set to all zero values. + * + * @param str The string to parse. + * @param out The value to populate if successful. + * + * @return True if a valid Vector4 was parsed, false otherwise. + */ + static bool parseVec4(const char* str, Vec4* out); + + /** + * Attempts to parse the specified string as an axis-angle value. + * + * The specified string is expected to contain four comma-separated + * values, where the first three values represents the axis and the + * fourth value represents the angle, in degrees. + * + * On error, false is returned and the output is set to all zero values. + * + * @param str The string to parse. + * @param out A Quaternion populated with the orientation of the axis-angle, if successful. + * + * @return True if a valid axis-angle was parsed, false otherwise. + */ + static bool parseAxisAngle(const char* str, Quaternion* out); + + /** + * Atempts to parse the specified string as an RGB color value. + * + * @param str The string to parse. + * @param out The value to populate if successful. + * + * @return True if a valid RGB color was parsed, false otherwise. + */ + static bool parseColor(const char* str, Vec3* out); + + /** + * Atempts to parse the specified string as an RGBA color value. + * + * @param str The string to parse. + * @param out The value to populate if successful. + * + * @return True if a valid RGBA color was parsed, false otherwise. + */ + static bool parseColor(const char* str, Vec4* out); + +private: + + /** + * Internal structure containing a single property. + */ + struct Property + { + std::string name; + std::string value; + Property(const std::string& aname, const std::string& avalue) : name(aname), value(avalue) { } + }; + + /** + * Constructor. + */ + Properties(); + + /** + * Constructs the Properties class from a file. + * + * @param stream The stream used for reading the properties from file. + */ + Properties(Data* data, ssize_t* dataIdx); + Properties(const Properties& copy); + + /** + * Constructor. Read from the beginning of namespace specified. + */ + Properties(Data* data, ssize_t* dataIdx, const std::string& name, const char* id, const char* parentID, Properties* parent); + + // Data manipulation methods + void readProperties(); + void skipWhiteSpace(); + char* trimWhiteSpace(char* str); + signed char readChar(); + char* readLine(char* output, int num); + bool seekFromCurrent(int offset); + bool eof(); + + // Called after create(); copies info from parents into derived namespaces. + void resolveInheritance(const char* id = NULL); + + // Called by resolveInheritance(). + void mergeWith(Properties* overrides); + + // Clones the Properties object. + Properties* clone(); + + void setDirectoryPath(const std::string* path); + void setDirectoryPath(const std::string& path); + + /** + * Reads the next character from the Data. Returns EOF if the end of the Data is reached. + */ + + // XXX: hack in order to simulate GamePlay's Stream with Cocos2d's Data + ssize_t *_dataIdx; + Data *_data; + + std::string _namespace; + std::string _id; + std::string _parentID; + std::list _properties; + std::list::iterator _propertiesItr; + std::vector _namespaces; + std::vector::const_iterator _namespacesItr; + std::vector* _variables; + std::string* _dirPath; + Properties* _parent; +}; + +} + +#endif // __cocos2d_libs__CCProperties__ diff --git a/cocos/base/CMakeLists.txt b/cocos/base/CMakeLists.txt index fa1dca6eab..9d6cee3a81 100644 --- a/cocos/base/CMakeLists.txt +++ b/cocos/base/CMakeLists.txt @@ -8,10 +8,6 @@ endif() # todo: also base/CCController-android.cpp set(COCOS_BASE_SRC - base/allocator/CCAllocatorDiagnostics.cpp - base/allocator/CCAllocatorGlobal.cpp - base/allocator/CCAllocatorGlobalNewDelete.cpp - base/ccFPSImages.c base/CCAsyncTaskPool.cpp base/CCAutoreleasePool.cpp base/CCConfiguration.cpp @@ -40,6 +36,7 @@ set(COCOS_BASE_SRC base/CCIMEDispatcher.cpp base/CCNS.cpp base/CCProfiling.cpp + base/CCProperties.cpp base/CCRef.cpp base/CCScheduler.cpp base/CCScriptSupport.cpp @@ -49,9 +46,13 @@ set(COCOS_BASE_SRC base/ObjectFactory.cpp base/TGAlib.cpp base/ZipUtils.cpp + base/allocator/CCAllocatorDiagnostics.cpp + base/allocator/CCAllocatorGlobal.cpp + base/allocator/CCAllocatorGlobalNewDelete.cpp base/atitc.cpp base/base64.cpp base/ccCArray.cpp + base/ccFPSImages.c base/ccRandom.cpp base/ccTypes.cpp base/ccUTF8.cpp diff --git a/cocos/cocos2d.cpp b/cocos/cocos2d.cpp index d785c1b4d5..0584742ae5 100644 --- a/cocos/cocos2d.cpp +++ b/cocos/cocos2d.cpp @@ -31,7 +31,7 @@ NS_CC_BEGIN CC_DLL const char* cocos2dVersion() { - return "cocos2d-x 3.7-pre"; + return "cocos2d-x 3.7-github"; } NS_CC_END diff --git a/cocos/cocos2d.h b/cocos/cocos2d.h index fb967bd7cf..03e24facd6 100644 --- a/cocos/cocos2d.h +++ b/cocos/cocos2d.h @@ -38,108 +38,108 @@ THE SOFTWARE. #include "base/ccConfig.h" // base +#include "base/CCAsyncTaskPool.h" +#include "base/CCAutoreleasePool.h" +#include "base/CCConfiguration.h" +#include "base/CCConsole.h" +#include "base/CCData.h" +#include "base/CCDirector.h" +#include "base/CCIMEDelegate.h" +#include "base/CCIMEDispatcher.h" +#include "base/CCMap.h" +#include "base/CCNS.h" +#include "base/CCProfiling.h" +#include "base/CCProperties.h" #include "base/CCRef.h" #include "base/CCRefPtr.h" -#include "base/CCVector.h" -#include "base/CCMap.h" -#include "base/CCAutoreleasePool.h" -#include "base/CCNS.h" -#include "base/CCData.h" +#include "base/CCScheduler.h" +#include "base/CCUserDefault.h" #include "base/CCValue.h" +#include "base/CCVector.h" +#include "base/ZipUtils.h" +#include "base/base64.h" #include "base/ccConfig.h" #include "base/ccMacros.h" #include "base/ccTypes.h" -#include "base/CCConfiguration.h" -#include "base/CCDirector.h" -#include "base/CCScheduler.h" -#include "base/base64.h" -#include "base/ZipUtils.h" -#include "base/CCProfiling.h" -#include "base/CCConsole.h" #include "base/ccUTF8.h" -#include "base/CCUserDefault.h" -#include "base/CCIMEDelegate.h" -#include "base/CCIMEDispatcher.h" #include "base/ccUtils.h" -#include "base/CCAsyncTaskPool.h" // EventDispatcher -#include "base/CCEventType.h" -#include "base/CCEventDispatcher.h" -#include "base/CCEventListenerTouch.h" -#include "base/CCEventTouch.h" -#include "base/CCEventListenerKeyboard.h" -#include "base/CCEventKeyboard.h" -#include "base/CCEventListenerMouse.h" -#include "base/CCEventMouse.h" #include "base/CCEventAcceleration.h" -#include "base/CCEventListenerAcceleration.h" #include "base/CCEventCustom.h" -#include "base/CCEventListenerCustom.h" +#include "base/CCEventDispatcher.h" #include "base/CCEventFocus.h" +#include "base/CCEventKeyboard.h" +#include "base/CCEventListenerAcceleration.h" +#include "base/CCEventListenerCustom.h" #include "base/CCEventListenerFocus.h" +#include "base/CCEventListenerKeyboard.h" +#include "base/CCEventListenerMouse.h" +#include "base/CCEventListenerTouch.h" +#include "base/CCEventMouse.h" +#include "base/CCEventTouch.h" +#include "base/CCEventType.h" // math #include "math/CCAffineTransform.h" #include "math/CCGeometry.h" +#include "math/CCVertex.h" +#include "math/Mat4.h" +#include "math/MathUtil.h" +#include "math/Quaternion.h" #include "math/Vec2.h" #include "math/Vec3.h" #include "math/Vec4.h" -#include "math/Mat4.h" -#include "math/Quaternion.h" -#include "math/MathUtil.h" -#include "math/CCVertex.h" // actions #include "2d/CCAction.h" -#include "2d/CCActionInterval.h" #include "2d/CCActionCamera.h" -#include "2d/CCActionManager.h" -#include "2d/CCActionEase.h" -#include "2d/CCActionPageTurn3D.h" -#include "2d/CCActionGrid.h" -#include "2d/CCActionProgressTimer.h" -#include "2d/CCActionGrid3D.h" -#include "2d/CCActionTiledGrid.h" -#include "2d/CCActionInstant.h" -#include "2d/CCActionTween.h" #include "2d/CCActionCatmullRom.h" +#include "2d/CCActionEase.h" +#include "2d/CCActionGrid.h" +#include "2d/CCActionGrid3D.h" +#include "2d/CCActionInstant.h" +#include "2d/CCActionInterval.h" +#include "2d/CCActionManager.h" +#include "2d/CCActionPageTurn3D.h" +#include "2d/CCActionProgressTimer.h" +#include "2d/CCActionTiledGrid.h" +#include "2d/CCActionTween.h" #include "2d/CCTweenFunction.h" // 2d nodes -#include "2d/CCNode.h" -#include "2d/CCProtectedNode.h" #include "2d/CCAtlasNode.h" -#include "2d/CCDrawingPrimitives.h" +#include "2d/CCClippingNode.h" +#include "2d/CCClippingRectangleNode.h" #include "2d/CCDrawNode.h" -#include "2d/CCLabelAtlas.h" -#include "2d/CCLabelTTF.h" -#include "2d/CCLabelBMFont.h" -#include "2d/CCLabel.h" +#include "2d/CCDrawingPrimitives.h" #include "2d/CCFontFNT.h" +#include "2d/CCLabel.h" +#include "2d/CCLabelAtlas.h" +#include "2d/CCLabelBMFont.h" +#include "2d/CCLabelTTF.h" #include "2d/CCLayer.h" +#include "2d/CCMenu.h" +#include "2d/CCMenuItem.h" +#include "2d/CCMotionStreak.h" +#include "2d/CCNode.h" +#include "2d/CCNodeGrid.h" +#include "2d/CCParticleBatchNode.h" +#include "2d/CCParticleExamples.h" +#include "2d/CCParticleSystem.h" +#include "2d/CCParticleSystemQuad.h" +#include "2d/CCProgressTimer.h" +#include "2d/CCProtectedNode.h" +#include "2d/CCRenderTexture.h" #include "2d/CCScene.h" #include "2d/CCTransition.h" #include "2d/CCTransitionPageTurn.h" #include "2d/CCTransitionProgress.h" -#include "2d/CCMenu.h" -#include "2d/CCMenuItem.h" -#include "2d/CCClippingNode.h" -#include "2d/CCClippingRectangleNode.h" -#include "2d/CCMotionStreak.h" -#include "2d/CCProgressTimer.h" -#include "2d/CCRenderTexture.h" -#include "2d/CCNodeGrid.h" -#include "2d/CCParticleBatchNode.h" -#include "2d/CCParticleSystem.h" -#include "2d/CCParticleExamples.h" -#include "2d/CCParticleSystemQuad.h" // 2d utils +#include "2d/CCCamera.h" #include "2d/CCGrabber.h" #include "2d/CCGrid.h" - -#include "2d/CCCamera.h" #include "2d/CCLight.h" // include @@ -147,44 +147,44 @@ THE SOFTWARE. // renderer #include "renderer/CCCustomCommand.h" -#include "renderer/CCGroupCommand.h" -#include "renderer/CCQuadCommand.h" -#include "renderer/CCRenderCommand.h" -#include "renderer/CCRenderCommandPool.h" -#include "renderer/CCRenderer.h" #include "renderer/CCGLProgram.h" #include "renderer/CCGLProgramCache.h" #include "renderer/CCGLProgramState.h" -#include "renderer/ccGLStateCache.h" -#include "renderer/ccShaders.h" -#include "renderer/CCTexture2D.h" -#include "renderer/CCTextureCache.h" -#include "renderer/CCVertexIndexBuffer.h" -#include "renderer/CCVertexIndexData.h" +#include "renderer/CCGroupCommand.h" +#include "renderer/CCMaterial.h" +#include "renderer/CCPass.h" #include "renderer/CCPrimitive.h" #include "renderer/CCPrimitiveCommand.h" -#include "renderer/CCTrianglesCommand.h" -#include "renderer/CCMaterial.h" -#include "renderer/CCTechnique.h" -#include "renderer/CCPass.h" +#include "renderer/CCQuadCommand.h" +#include "renderer/CCRenderCommand.h" +#include "renderer/CCRenderCommandPool.h" #include "renderer/CCRenderState.h" +#include "renderer/CCRenderer.h" +#include "renderer/CCTechnique.h" +#include "renderer/CCTexture2D.h" +#include "renderer/CCTextureCache.h" +#include "renderer/CCTrianglesCommand.h" +#include "renderer/CCVertexIndexBuffer.h" +#include "renderer/CCVertexIndexData.h" +#include "renderer/ccGLStateCache.h" +#include "renderer/ccShaders.h" // physics #include "physics/CCPhysicsBody.h" #include "physics/CCPhysicsContact.h" -#include "physics/CCPhysicsShape.h" #include "physics/CCPhysicsJoint.h" +#include "physics/CCPhysicsShape.h" #include "physics/CCPhysicsWorld.h" // platform -#include "platform/CCDevice.h" #include "platform/CCCommon.h" +#include "platform/CCDevice.h" #include "platform/CCFileUtils.h" #include "platform/CCImage.h" -#include "platform/CCSAXParser.h" -#include "platform/CCThread.h" #include "platform/CCPlatformConfig.h" #include "platform/CCPlatformMacros.h" +#include "platform/CCSAXParser.h" +#include "platform/CCThread.h" #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #include "platform/ios/CCApplication-ios.h" @@ -253,14 +253,14 @@ THE SOFTWARE. #include "renderer/CCTextureAtlas.h" // tilemap_parallax_nodes +#include "2d/CCFastTMXLayer.h" +#include "2d/CCFastTMXTiledMap.h" #include "2d/CCParallaxNode.h" #include "2d/CCTMXLayer.h" #include "2d/CCTMXObjectGroup.h" #include "2d/CCTMXTiledMap.h" #include "2d/CCTMXXMLParser.h" #include "2d/CCTileMapAtlas.h" -#include "2d/CCFastTMXTiledMap.h" -#include "2d/CCFastTMXLayer.h" // component #include "2d/CCComponent.h" @@ -268,34 +268,34 @@ THE SOFTWARE. //3d #include "3d/CCAABB.h" -#include "3d/CCOBB.h" -#include "3d/CCRay.h" -#include "3d/CCSprite3D.h" -#include "3d/CCMesh.h" -#include "3d/CCMeshSkin.h" #include "3d/CCAnimate3D.h" #include "3d/CCAnimation3D.h" -#include "3d/CCSprite3DMaterial.h" #include "3d/CCAttachNode.h" -#include "3d/CCMeshVertexIndexData.h" -#include "3d/CCSkeleton3D.h" #include "3d/CCBillBoard.h" #include "3d/CCFrustum.h" +#include "3d/CCMesh.h" +#include "3d/CCMeshSkin.h" +#include "3d/CCMeshVertexIndexData.h" +#include "3d/CCOBB.h" #include "3d/CCPlane.h" -#include "3d/CCTextureCube.h" +#include "3d/CCRay.h" +#include "3d/CCSkeleton3D.h" #include "3d/CCSkybox.h" +#include "3d/CCSprite3D.h" +#include "3d/CCSprite3DMaterial.h" #include "3d/CCTerrain.h" +#include "3d/CCTextureCube.h" // Deprecated include -#include "deprecated/CCDictionary.h" #include "deprecated/CCArray.h" -#include "deprecated/CCSet.h" -#include "deprecated/CCInteger.h" -#include "deprecated/CCFloat.h" -#include "deprecated/CCDouble.h" #include "deprecated/CCBool.h" -#include "deprecated/CCString.h" +#include "deprecated/CCDictionary.h" +#include "deprecated/CCDouble.h" +#include "deprecated/CCFloat.h" +#include "deprecated/CCInteger.h" #include "deprecated/CCNotificationCenter.h" +#include "deprecated/CCSet.h" +#include "deprecated/CCString.h" // CCDeprecated.h must be included at the end #include "deprecated/CCDeprecated.h" diff --git a/cocos/renderer/CCMaterial.cpp b/cocos/renderer/CCMaterial.cpp index 57bb9f8ed8..5635b3fffe 100644 --- a/cocos/renderer/CCMaterial.cpp +++ b/cocos/renderer/CCMaterial.cpp @@ -34,25 +34,21 @@ #include "renderer/CCTexture2D.h" #include "renderer/CCGLProgram.h" #include "renderer/CCGLProgramState.h" - +#include "base/CCProperties.h" #include "base/CCDirector.h" #include "platform/CCFileUtils.h" -#include "json/document.h" #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) #define strcasecmp _stricmp #endif -static const float MATERIAL_FORMAT_VERSION = 1.0; -static const char* MATERIAL_TYPE = "material"; +NS_CC_BEGIN // Helpers declaration -static const char* getOptionalString(const rapidjson::GenericValue >& json, const char* key, const char* defaultValue); +static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue); static bool isValidUniform(const char* name); -NS_CC_BEGIN - Material* Material::createWithFilename(const std::string& filepath) { auto validfilename = FileUtils::getInstance()->fullPathForFilename(filepath); @@ -68,6 +64,17 @@ Material* Material::createWithFilename(const std::string& filepath) return nullptr; } +Material* Material::createWithProperties(Properties* materialProperties) +{ + auto mat = new (std::nothrow) Material(); + if (mat && mat->initWithProperties(materialProperties)) + { + mat->autorelease(); + return mat; + } + return nullptr; +} + Material* Material::createWithGLStateProgram(GLProgramState* programState) { CCASSERT(programState, "Invalid GL Program State"); @@ -102,68 +109,42 @@ bool Material::initWithFile(const std::string& validfilename) char* bytes = (char*)data.getBytes(); bytes[data.getSize()-1]='\0'; - rapidjson::Document document; - document.ParseInsitu<0>(bytes); + Properties* properties = Properties::create(validfilename); - if (document.HasParseError()) - { - CCLOG("GetParseError %s\n", document.GetParseError()); - return false; - } - - CCASSERT(document.IsObject(), "Invalid JSON file"); - - if (! parseMetadata(document)) { - CCLOG("Error parsing Material metadata"); - return false; - } - - parseProperties(document); + // get the first material + parseProperties((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace()); return true; } +bool Material::initWithProperties(Properties* materialProperties) +{ + return parseProperties(materialProperties); +} + void Material::setTarget(cocos2d::Node *target) { _target = target; } -bool Material::parseMetadata(const rapidjson::Document& jsonDocument) +bool Material::parseProperties(Properties* materialProperties) { - bool broken = false; - - const auto& metadata = jsonDocument["metadata"]; - if (metadata.IsObject()) + setName(materialProperties->getId()); + auto space = materialProperties->getNextNamespace(); + while (space) { - auto version = metadata["version"].GetDouble(); - broken |= std::floor(version) != std::floor(MATERIAL_FORMAT_VERSION); + const char* name = space->getNamespace(); + if (strcmp(name, "technique") == 0) { + parseTechnique(space); + } + + space = materialProperties->getNextNamespace(); - auto type = metadata["type"].GetString(); - broken |= strcmp(type, MATERIAL_TYPE) != 0; } - - return !broken; -} - -bool Material::parseProperties(const rapidjson::Document& jsonDocument) -{ - auto name = jsonDocument["name"].GetString(); - setName(name); - - auto& techniquesJSON = jsonDocument["techniques"]; - CCASSERT(techniquesJSON.IsArray(), "Invalid Techniques"); - - for (rapidjson::SizeType i = 0; i < techniquesJSON.Size(); i++) { - auto& techniqueJSON = techniquesJSON[i]; - parseTechnique(techniqueJSON); - } - return true; } -bool Material::parseTechnique(const rapidjson::GenericValue >& techniqueJSON) +bool Material::parseTechnique(Properties* techniqueProperties) { - CCASSERT(techniqueJSON.IsObject(), "Invalid type for Technique. It must be an object"); - auto technique = Technique::create(this); _techniques.pushBack(technique); @@ -172,56 +153,60 @@ bool Material::parseTechnique(const rapidjson::GenericValue >& _currentTechnique = technique; // name - if (techniqueJSON.HasMember("name")) - technique->setName(techniqueJSON["name"].GetString()); + technique->setName(techniqueProperties->getId()); + // passes - auto& passesJSON = techniqueJSON["passes"]; - CCASSERT(passesJSON.IsArray(), "Invalid type for 'passes'"); + auto space = techniqueProperties->getNextNamespace(); + while (space) + { + const char* name = space->getNamespace(); + if (strcmp(name, "pass") == 0) { + parsePass(technique, space); + } - for (rapidjson::SizeType i = 0; i < passesJSON.Size(); i++) { - auto& passJSON = passesJSON[i]; - parsePass(technique, passJSON); + space = techniqueProperties->getNextNamespace(); } return true; } -bool Material::parsePass(Technique* technique, const rapidjson::GenericValue >& passJSON) +bool Material::parsePass(Technique* technique, Properties* passProperties) { auto pass = Pass::create(technique); technique->addPass(pass); - // Textures - if (passJSON.HasMember("textures")) { - auto& texturesJSON = passJSON["textures"]; - CCASSERT(texturesJSON.IsArray(), "Invalid type for 'textures'"); + // Pass can have 3 different namespaces: + // - one or more "sampler" + // - one "renderState" + // - one "shader" - for (rapidjson::SizeType i = 0; i < texturesJSON.Size(); i++) { - auto& textureJSON = texturesJSON[i]; - parseTexture(pass, textureJSON); + auto space = passProperties->getNextNamespace(); + while (space) + { + const char* name = space->getNamespace(); + if (strcmp(name, "sampler") == 0) + parseSampler(pass, space); + else if (strcmp(name, "shader") == 0) + parseShader(pass, space); + else if (strcmp(name, "renderState") == 0) + parseRenderState(pass, space); + else { + CCASSERT(false, "Invalid namespace"); + return false; } - } - // Render State - if (passJSON.HasMember("renderState")) { - parseRenderState(pass, passJSON["renderState"]); - } - - // Shaders - if (passJSON.HasMember("shader")) { - parseShader(pass, passJSON["shader"]); + space = passProperties->getNextNamespace(); } return true; } -bool Material::parseTexture(Pass* pass, const rapidjson::GenericValue >& textureJSON) +// cocos2d-x doesn't support Samplers yet. But will be added soon +bool Material::parseSampler(Pass* pass, Properties* textureProperties) { - CCASSERT(textureJSON.IsObject(), "Invalid type for Texture. It must be an object"); - // required - auto filename = textureJSON["path"].GetString(); + auto filename = textureProperties->getString("path"); auto texture = Director::getInstance()->getTextureCache()->addImage(filename); if (!texture) { @@ -236,14 +221,14 @@ bool Material::parseTexture(Pass* pass, const rapidjson::GenericValuegenerateMipmap(); usemipmap = true; } // valid options: REPEAT, CLAMP - const char* wrapS = getOptionalString(textureJSON, "wrapS", "CLAMP_TO_EDGE"); + const char* wrapS = getOptionalString(textureProperties, "wrapS", "CLAMP_TO_EDGE"); if (strcasecmp(wrapS, "REPEAT")==0) texParams.wrapS = GL_REPEAT; else if(strcasecmp(wrapS, "CLAMP_TO_EDGE")==0) @@ -253,7 +238,7 @@ bool Material::parseTexture(Pass* pass, const rapidjson::GenericValue >& shaderJSON) +bool Material::parseShader(Pass* pass, Properties* shaderProperties) { - - CCASSERT(shaderJSON.IsObject(), "Invalid type for 'shader'. It must be an object"); - // vertexShader - const char* vertShader = getOptionalString(shaderJSON, "vertexShader", nullptr); + const char* vertShader = getOptionalString(shaderProperties, "vertexShader", nullptr); // fragmentShader - const char* fragShader = getOptionalString(shaderJSON, "fragmentShader", nullptr); + const char* fragShader = getOptionalString(shaderProperties, "fragmentShader", nullptr); // compileTimeDefines - const char* compileTimeDefines = getOptionalString(shaderJSON, "defines", ""); + const char* compileTimeDefines = getOptionalString(shaderProperties, "defines", ""); if (vertShader && fragShader) { @@ -315,11 +297,15 @@ bool Material::parseShader(Pass* pass, const rapidjson::GenericValuesetGLProgramState(glProgramState); // Parse uniforms only if the GLProgramState was created - for( auto it = shaderJSON.MemberonBegin(); it != shaderJSON.MemberonEnd(); it++) + auto property = shaderProperties->getNextProperty(); + while (property) { - // skip "defines", "vertexShader", "fragmentShader" - if (isValidUniform(it->name.GetString())) - parseUniform(glProgramState, it); + if (isValidUniform(property)) + { + parseUniform(glProgramState, shaderProperties, property); + } + + property = shaderProperties->getNextProperty(); } // glProgramState->updateUniformsAndAttributes(); @@ -328,107 +314,75 @@ bool Material::parseShader(Pass* pass, const rapidjson::GenericValuename.GetString(); - auto& value = iterator->value; + auto type = properties->getType(uniformName); - if (value.IsNumber()) { - float v = value.GetDouble(); - programState->setUniformFloat(key, v); - } - else if (value.IsArray()) { - - int size = value.Size(); - switch (size) { - case 1: - { - rapidjson::SizeType idx = 0; - float v = value[idx].GetDouble(); - programState->setUniformFloat(key, v); - break; - } - case 2: - { - Vec2 vect = parseUniformVec2(value); - programState->setUniformVec2(key, vect); - break; - } - case 3: - { - Vec3 vect = parseUniformVec3(value); - programState->setUniformVec3(key, vect); - break; - } - case 4: - { - Vec4 vect = parseUniformVec4(value); - programState->setUniformVec4(key, vect); - break; - } - case 16: - { - Mat4 mat = parseUniformMat4(value); - programState->setUniformMat4(key, mat); - break; - } - default: - break; + switch (type) { + case Properties::Type::NUMBER: + { + auto f = properties->getFloat(uniformName); + programState->setUniformFloat(uniformName, f); + break; } + case Properties::Type::STRING: + CCASSERT(false, "invalid type for a uniform"); + return false; + break; + + case Properties::Type::VECTOR2: + { + Vec2 v2; + properties->getVec2(uniformName, &v2); + programState->setUniformVec2(uniformName, v2); + break; + } + + case Properties::Type::VECTOR3: + { + Vec3 v3; + properties->getVec3(uniformName, &v3); + programState->setUniformVec3(uniformName, v3); + break; + } + + case Properties::Type::VECTOR4: + { + Vec4 v4; + properties->getVec4(uniformName, &v4); + programState->setUniformVec4(uniformName, v4); + break; + } + + case Properties::Type::MATRIX: + { + Mat4 m4; + properties->getMat4(uniformName, &m4); + programState->setUniformMat4(uniformName, m4); + break; + } + + default: + return false; + break; } return true; } -Vec2 Material::parseUniformVec2(const rapidjson::GenericValue >& value) -{ - Vec2 ret; - rapidjson::SizeType idx = 0; - ret.x = value[idx++].GetDouble(); - ret.y = value[idx++].GetDouble(); - return ret; -} -Vec3 Material::parseUniformVec3(const rapidjson::GenericValue >& value) -{ - Vec3 ret; - rapidjson::SizeType idx = 0; - ret.x = value[idx++].GetDouble(); - ret.y = value[idx++].GetDouble(); - ret.z = value[idx++].GetDouble(); - return ret; -} - -Vec4 Material::parseUniformVec4(const rapidjson::GenericValue >& value) -{ - Vec4 ret; - rapidjson::SizeType idx = 0; - ret.x = value[idx++].GetDouble(); - ret.y = value[idx++].GetDouble(); - ret.z = value[idx++].GetDouble(); - ret.w = value[idx++].GetDouble(); - return ret; -} - -Mat4 Material::parseUniformMat4(const rapidjson::GenericValue >& value) -{ - Mat4 ret; - - for(rapidjson::SizeType i= 0; i<16; i++) - ret.m[i] = value[i].GetDouble(); - - return ret; -} - -bool Material::parseRenderState(Pass* pass, const rapidjson::GenericValue >& renderState) +bool Material::parseRenderState(Pass* pass, Properties* properties) { auto state = pass->getStateBlock(); - // Parse uniforms only if the GLProgramState was created - for( auto it = renderState.MemberonBegin(); it != renderState.MemberonEnd(); it++) + auto property = properties->getNextProperty(); + while (property) { - // Render state only can have "strings" or numbers as values. No objects or lists - state->setState(it->name.GetString(), it->value.GetString()); + // Parse uniforms only if the GLProgramState was created + // Render state only can have "strings" or numbers as values. No new namespaces + state->setState(property, properties->getString(property)); + + property = properties->getNextProperty(); } return true; @@ -459,10 +413,15 @@ Technique* Material::getTechnique() const { return _currentTechnique; } + +const Vector& Material::getTechniques() const +{ + return _techniques; +} + Technique* Material::getTechniqueByName(const std::string& name) { for(const auto& technique : _techniques) { - CCLOG("technique name: %s", technique->getName().c_str()); if (technique->getName().compare(name)==0) return technique; } @@ -493,7 +452,6 @@ ssize_t Material::getTechniqueCount() const return _techniques.size(); } -NS_CC_END // Helpers implementation static bool isValidUniform(const char* name) @@ -503,11 +461,14 @@ static bool isValidUniform(const char* name) strcmp(name, "fragmentShader")==0); } -static const char* getOptionalString(const rapidjson::GenericValue >& json, const char* key, const char* defaultValue) +static const char* getOptionalString(Properties* properties, const char* key, const char* defaultValue) { - if (json.HasMember(key)) { - return json[key].GetString(); - } - return defaultValue; + + const char* ret = properties->getString(key); + if (!ret) + ret = defaultValue; + + return ret; } +NS_CC_END diff --git a/cocos/renderer/CCMaterial.h b/cocos/renderer/CCMaterial.h index c029878908..5274ad5766 100644 --- a/cocos/renderer/CCMaterial.h +++ b/cocos/renderer/CCMaterial.h @@ -31,7 +31,6 @@ #define __cocos2d_libs__CCMaterial__ #include -#include "json/document.h" #include "renderer/CCRenderState.h" #include "renderer/CCTechnique.h" @@ -50,6 +49,7 @@ class Technique; class Pass; class GLProgramState; class Node; +class Properties; /// Material class CC_DLL Material : public RenderState @@ -62,7 +62,14 @@ class CC_DLL Material : public RenderState friend class Mesh; public: - /** Creates a Material with a JSON file containing the definition of the material. + /** + * Creates a Material using the data from the Properties object defined at the specified URL, + * where the URL is of the format ".#//.../" + * (and "#//.../" is optional). + * + * @param url The URL pointing to the Properties object defining the material. + * + * @return A new Material or NULL if there was an error. */ static Material* createWithFilename(const std::string& path); @@ -72,6 +79,15 @@ public: */ static Material* createWithGLStateProgram(GLProgramState* programState); + /** + * Creates a material from the specified properties object. + * + * @param materialProperties The properties object defining the + * material (must have namespace equal to 'material'). + * @return A new Material. + */ + static Material* createWithProperties(Properties* materialProperties); + /// returns the material name std::string getName() const; /// sets the material name @@ -87,38 +103,38 @@ public: */ Technique* getTechniqueByIndex(ssize_t index); + /** Returns the Technique used by the Material */ + Technique* getTechnique() const; + + /** Returns the list of Techniques */ + const Vector& getTechniques() const; + + /** Returns the number of Techniques in the Material. */ + ssize_t getTechniqueCount() const; + /** Adds a Technique into the Material */ void addTechnique(Technique* technique); /** Sets the current technique */ void setTechnique(const std::string& techniqueName); - /** Returns the number of Techniques in the Material. */ - ssize_t getTechniqueCount() const; - - /** Returns the Technique used by the Material */ - Technique* getTechnique() const; protected: Material(); ~Material(); bool initWithGLProgramState(GLProgramState* state); bool initWithFile(const std::string& file); + bool initWithProperties(Properties* materialProperties); void setTarget(Node* target); - bool parseMetadata(const rapidjson::Document& json); - bool parseProperties(const rapidjson::Document& json); - bool parseTechnique(const rapidjson::GenericValue >& techniqueJSON); - bool parsePass(Technique* technique, const rapidjson::GenericValue >& passJSON); - bool parseTexture(Pass* pass, const rapidjson::GenericValue >& textureJSON); - bool parseShader(Pass* pass, const rapidjson::GenericValue >& shaderJSON); - bool parseUniform(GLProgramState* programState, const rapidjson::Value::ConstMemberIterator& iterator); - Vec2 parseUniformVec2(const rapidjson::GenericValue >& uniformJSON); - Vec3 parseUniformVec3(const rapidjson::GenericValue >& uniformJSON); - Vec4 parseUniformVec4(const rapidjson::GenericValue >& uniformJSON); - Mat4 parseUniformMat4(const rapidjson::GenericValue >& uniformJSON); - bool parseRenderState(Pass* pass, const rapidjson::GenericValue >& renderState); + bool parseProperties(Properties* properties); + bool parseTechnique(Properties* properties); + bool parsePass(Technique* technique, Properties* properties); + bool parseSampler(Pass* pass, Properties* properties); + bool parseShader(Pass* pass, Properties* properties); + bool parseUniform(GLProgramState* programState, Properties* properties, const char* uniformName); + bool parseRenderState(Pass* pass, Properties* properties); // material name diff --git a/cocos/renderer/CCTechnique.cpp b/cocos/renderer/CCTechnique.cpp index e006780058..28023e8689 100644 --- a/cocos/renderer/CCTechnique.cpp +++ b/cocos/renderer/CCTechnique.cpp @@ -100,4 +100,9 @@ ssize_t Technique::getPassCount() const return _passes.size(); } +const Vector& Technique::getPasses() const +{ + return _passes; +} + NS_CC_END \ No newline at end of file diff --git a/cocos/renderer/CCTechnique.h b/cocos/renderer/CCTechnique.h index 5c7f0b8aee..d893e878f4 100644 --- a/cocos/renderer/CCTechnique.h +++ b/cocos/renderer/CCTechnique.h @@ -73,6 +73,9 @@ public: /** Returns the number of Passes in the Technique */ ssize_t getPassCount() const; + /** Returns the list of passes */ + const Vector& getPasses() const; + protected: Technique(); ~Technique(); diff --git a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp index ad372eef32..8656cef895 100644 --- a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp +++ b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.cpp @@ -30,16 +30,50 @@ USING_NS_CC; -MaterialSystemTest::MaterialSystemTest() +// MARK: Helper functions + +static void printProperties(Properties* properties, int indent) { - ADD_TEST_CASE(Material_MultipleSprite3D); - ADD_TEST_CASE(Material_Sprite3DTest); + // Print the name and ID of the current namespace. + const char* spacename = properties->getNamespace(); + const char* id = properties->getId(); + char chindent[64]; + int i=0; + for(i=0; igetNextProperty(); + const char* value = NULL; + while (name != NULL) + { + value = properties->getString(name); + CCLOG("%s%s = %s", chindent, name, value); + name = properties->getNextProperty(); + } + + Properties* space = properties->getNextNamespace(); + while (space != NULL) + { + printProperties(space, indent+1); + space = properties->getNextNamespace(); + } + + CCLOG("%s}\n",chindent); } -// MARK: +// MARK: Tests + +MaterialSystemTest::MaterialSystemTest() +{ + ADD_TEST_CASE(Material_2DEffects); + ADD_TEST_CASE(Material_3DEffects); + ADD_TEST_CASE(Material_MultipleSprite3D); + ADD_TEST_CASE(Material_Sprite3DTest); +} std::string MaterialSystemBaseTest::title() const { @@ -48,36 +82,6 @@ std::string MaterialSystemBaseTest::title() const // MARK: Tests start here -void Material_SpriteTest::onEnter() -{ - // Material remove from Sprite since it is hacking. - // Sprite (or Node) should have "Effect" instead of "Material" - - MaterialSystemBaseTest::onEnter(); - auto layer = LayerColor::create(Color4B::BLUE); - this->addChild(layer); - - - auto sprite = Sprite::create("Images/grossini.png"); - sprite->setNormalizedPosition(Vec2(0.5, 0.5)); - this->addChild(sprite); - -// auto material = Material::createWithFilename("Materials/effects.material"); -// sprite->setMaterial(material); - -// material->setTechnique("blur"); -// material->setTechnique("outline"); -// material->setTechnique("noise"); -// material->setTechnique("edge detect"); -// material->setTechnique("gray+blur"); -} - -std::string Material_SpriteTest::subtitle() const -{ - return "Material System on Sprite"; -} - - void Material_Sprite3DTest::onEnter() { MaterialSystemBaseTest::onEnter(); @@ -87,9 +91,6 @@ void Material_Sprite3DTest::onEnter() sprite->setTexture("Sprite3DTest/boss.png"); this->addChild(sprite); sprite->setNormalizedPosition(Vec2(0.5,0.5)); - -// auto material = Material::createWithFilename("Materials/spaceship.material"); -// sprite->setMaterial(material); } std::string Material_Sprite3DTest::subtitle() const @@ -97,10 +98,6 @@ std::string Material_Sprite3DTest::subtitle() const return "Material System on Sprite3D"; } - -// -// -// void Material_MultipleSprite3D::onEnter() { MaterialSystemBaseTest::onEnter(); @@ -134,3 +131,102 @@ std::string Material_MultipleSprite3D::subtitle() const return "Sprites with multiple meshes"; } +// +// +// +void Material_2DEffects::onEnter() +{ + MaterialSystemBaseTest::onEnter(); + + auto properties = Properties::create("Materials/2d_effects.material#sample"); + + // Print the properties of every namespace within this one. + printProperties(properties, 0); + + Material *mat1 = Material::createWithProperties(properties); + + auto spriteBlur = Sprite::create("Images/grossini.png"); + spriteBlur->setNormalizedPosition(Vec2(0.2, 0.5)); + this->addChild(spriteBlur); + spriteBlur->setGLProgramState(mat1->getTechniqueByName("blur")->getPassByIndex(0)->getGLProgramState()); + + auto spriteOutline = Sprite::create("Images/grossini.png"); + spriteOutline->setNormalizedPosition(Vec2(0.4, 0.5)); + this->addChild(spriteOutline); + spriteOutline->setGLProgramState(mat1->getTechniqueByName("outline")->getPassByIndex(0)->getGLProgramState()); + + auto spriteNoise = Sprite::create("Images/grossini.png"); + spriteNoise->setNormalizedPosition(Vec2(0.6, 0.5)); + this->addChild(spriteNoise); + spriteNoise->setGLProgramState(mat1->getTechniqueByName("noise")->getPassByIndex(0)->getGLProgramState()); + + auto spriteEdgeDetect = Sprite::create("Images/grossini.png"); + spriteEdgeDetect->setNormalizedPosition(Vec2(0.8, 0.5)); + this->addChild(spriteEdgeDetect); + spriteEdgeDetect->setGLProgramState(mat1->getTechniqueByName("edge_detect")->getPassByIndex(0)->getGLProgramState()); +} + +std::string Material_2DEffects::subtitle() const +{ + return "Testing effects on Sprite"; +} + +// +// +// +void Material_3DEffects::onEnter() +{ + MaterialSystemBaseTest::onEnter(); + + auto sprite = Sprite3D::create("Sprite3DTest/boss1.obj"); + sprite->setScale(8.f); +// sprite->setTexture("Sprite3DTest/boss.png"); + this->addChild(sprite); + sprite->setNormalizedPosition(Vec2(0.5,0.5)); + _sprite = sprite; + + auto rot = RotateBy::create(5, Vec3(30,60,270)); + auto repeat = RepeatForever::create(rot); + sprite->runAction(repeat); + + Material *mat = Material::createWithFilename("Materials/3d_effects.material"); + mat->setTechnique("lit"); + sprite->setMaterial(mat); + + // lights + auto light1 = AmbientLight::create(Color3B::RED); + addChild(light1); + + auto light2 = DirectionLight::create(Vec3(-1,1,0), Color3B::GREEN); + addChild(light2); + + this->schedule(CC_CALLBACK_1(Material_3DEffects::changeMaterial, this), 3, "cookie"); + + _techniqueState = 0; +} + +std::string Material_3DEffects::subtitle() const +{ + return "Testing effects on Sprite3D"; +} + +void Material_3DEffects::changeMaterial(float dt) +{ + // get it from Mesh 0 + switch (_techniqueState) + { + case 0: + _sprite->getMaterial(0)->setTechnique("lit"); + break; + case 1: + _sprite->getMaterial(0)->setTechnique("unlit"); + break; + default: + break; + } + + _techniqueState++; + if (_techniqueState>1) + _techniqueState = 0; +} + diff --git a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h index 7ff09279d5..fb2427f961 100644 --- a/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h +++ b/tests/cpp-tests/Classes/MaterialSystemTest/MaterialSystemTest.h @@ -35,15 +35,6 @@ public: virtual std::string title() const override; }; -class Material_SpriteTest : public MaterialSystemBaseTest -{ -public: - CREATE_FUNC(Material_SpriteTest); - - virtual void onEnter() override; - virtual std::string subtitle() const override; -}; - class Material_Sprite3DTest : public MaterialSystemBaseTest { public: @@ -62,4 +53,28 @@ public: virtual std::string subtitle() const override; }; +class Material_2DEffects : public MaterialSystemBaseTest +{ +public: + CREATE_FUNC(Material_2DEffects); + + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class Material_3DEffects : public MaterialSystemBaseTest +{ +public: + CREATE_FUNC(Material_3DEffects); + + virtual void onEnter() override; + virtual std::string subtitle() const override; + +private: + void changeMaterial(float dt); + + cocos2d::Sprite3D* _sprite; + int _techniqueState; +}; + diff --git a/tests/cpp-tests/Resources/Materials/2d_effects.material b/tests/cpp-tests/Resources/Materials/2d_effects.material new file mode 100644 index 0000000000..545ca512f5 --- /dev/null +++ b/tests/cpp-tests/Resources/Materials/2d_effects.material @@ -0,0 +1,170 @@ +material sample +{ + technique blur + { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + defines = THIS_IS_AN_EXAMPLE 1;TOMORROW_IS_HOLIDAY 2 + vertexShader = Shaders/example_simple.vsh + fragmentShader = Shaders/example_Blur.fsh + blurRadius = 10 + sampleNum = 5 + resolution = 100,100 + } + sampler 0 + { + path = Images/grossinis_sister1.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } + technique outline + { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + vertexShader = Shaders/example_simple.vsh + fragmentShader = Shaders/example_outline.fsh + u_outlineColor = 0.1, 0.2, 0.3 + u_radius = 0.01 + u_threshold = 1.75 + } + sampler 0 + { + path = Images/grossinis_sister1.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } + technique noise { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + vertexShader = Shaders/example_simple.vsh + fragmentShader = Shaders/example_Noisy.fsh + resolution = 100,100 + } + sampler 0 + { + path = Images/grossinis_sister1.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } + technique edge_detect + { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + defines = + vertexShader = Shaders/example_simple.vsh + fragmentShader = Shaders/example_edgeDetection.fsh + resolution = 100, 100 + } + sampler 0 + { + path = Images/grossinis_sister1.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } + technique gray+blur + { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + defines = TEXTURE_REPEAT + vertexShader = Shaders/example_simple.vsh + fragmentShader = Shaders/example_Blur.fsh + blurRadius = 10 + sampleNum = 5 + resolution = 100, 100 + } + sampler 0 + { + path = Images/grossinis_sister1.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + pass 1 + { + renderState + { + blend = true + blendSrc = ONE_MINUS_SRC_ALPHA + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + defines = + vertexShader = Shaders/example_simple.vsh + fragmentShader = Shaders/example_greyScale.fsh + } + sampler 0 + { + path = Images/grossinis_sister1.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } +} diff --git a/tests/cpp-tests/Resources/Materials/3d_effects.material b/tests/cpp-tests/Resources/Materials/3d_effects.material new file mode 100644 index 0000000000..a6d0c0a3b5 --- /dev/null +++ b/tests/cpp-tests/Resources/Materials/3d_effects.material @@ -0,0 +1,58 @@ +material spaceship +{ + technique unlit + { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + defines = THIS_IS_AN_EXAMPLE 1;TOMORROW_IS_HOLIDAY 2 + vertexShader = Shaders3D/3d_position_tex.vert + fragmentShader = Shaders3D/3d_color_tex.frag + } + sampler 0 + { + path = Sprite3DTest/boss.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } + + technique lit + { + pass 0 + { + renderState + { + blend = true + blendSrc = ONE + blendDst = ONE_MINUS_SRC_ALPHA + } + shader + { + defines = MAX_POINT_LIGHT_NUM 1;MAX_SPOT_LIGHT_NUM 1;MAX_DIRECTIONAL_LIGHT_NUM 1 + vertexShader = Shaders3D/3d_position_normal_tex.vert + fragmentShader = Shaders3D/3d_color_normal_tex.frag + } + sampler 0 + { + path = Sprite3DTest/boss.png + wrapS = CLAMP_TO_EDGE + wrapT = CLAMP_TO_EDGE + minFilter = LINEAR + magFilter = LINEAR + mipmap = false + } + } + } +} diff --git a/tests/cpp-tests/Resources/Materials/effects.material b/tests/cpp-tests/Resources/Materials/effects.material deleted file mode 100644 index bb5ff81a6f..0000000000 --- a/tests/cpp-tests/Resources/Materials/effects.material +++ /dev/null @@ -1,177 +0,0 @@ -{ - "metadata" : { - "version" : 1, - "type" : "material" - }, - "name" : "simple effects", - "techniques" : [ - { - "name": "blur", - "passes": [ - { - "renderState": { - "blend": "true", - "blendSrc": "ONE", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "THIS_IS_AN_EXAMPLE 1;TOMORROW_IS_HOLIDAY 2", - "vertexShader": "Shaders/example_simple.vsh", - "fragmentShader": "Shaders/example_Blur.fsh", - "blurRadius": 10, - "sampleNum": 5, - "resolution": [100,100] - }, - "textures": [ - { - "path": "Images/grossinis_sister1.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - } - ] - }, - { - "name": "outline", - "passes": [ - { - "renderState": { - "blend": "true", - "blendSrc": "ONE", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "", - "vertexShader": "Shaders/example_simple.vsh", - "fragmentShader": "Shaders/example_outline.fsh", - "u_outlineColor": [0.1, 0.2, 0.3], - "u_radius": 0.01, - "u_threshold": 1.75 - }, - "textures": [ - { - "path": "Images/grossinis_sister1.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - } - ] - }, - { - "name": "noise", - "passes": [ - { - "renderState": { - "blend": "true", - "blendSrc": "ONE", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "", - "vertexShader": "Shaders/example_simple.vsh", - "fragmentShader": "Shaders/example_Noisy.fsh", - "resolution": [100,100] - }, - "textures": [ - { - "path": "Images/grossinis_sister1.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - } - ] - }, - { - "name": "edge detect", - "passes": [ - { - "renderState": { - "blend": "true", - "blendSrc": "ONE", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "", - "vertexShader": "Shaders/example_simple.vsh", - "fragmentShader": "Shaders/example_edgeDetection.fsh", - "resolution": [100,100] - }, - "textures": [ - { - "path": "Images/grossinis_sister1.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - } - ] - }, - { - "name": "gray+blur", - "passes": [ - { - "renderState": { - "blend": "true", - "blendSrc": "ONE", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "TEXTURE_REPEAT", - "vertexShader": "Shaders/example_simple.vsh", - "fragmentShader": "Shaders/example_Blur.fsh", - "blurRadius": 10, - "sampleNum": 5, - "resolution": [100,100] - }, - "textures": [ - { - "path": "Images/grossinis_sister1.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - }, - { - "renderState": { - "blend": "true", - "blendSrc": "ONE_MINUS_SRC_ALPHA", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "", - "vertexShader": "Shaders/example_simple.vsh", - "fragmentShader": "Shaders/example_greyScale.fsh" - }, - "textures": [ - { - "path": "Images/grossinis_sister1.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - } - ] - } - ] -} diff --git a/tests/cpp-tests/Resources/Materials/spaceship.material b/tests/cpp-tests/Resources/Materials/spaceship.material deleted file mode 100644 index 285a1ea5e3..0000000000 --- a/tests/cpp-tests/Resources/Materials/spaceship.material +++ /dev/null @@ -1,37 +0,0 @@ -{ - "metadata" : { - "version" : 1, - "type" : "material" - }, - "name" : "spaceship", - "techniques" : [ - { - "name": "simple", - "passes": [ - { - "renderState": { - "blend": "true", - "blendSrc": "ONE_MINUS_SRC_ALPHA", - "blendDst": "ONE_MINUS_SRC_ALPHA" - }, - "shader" : { - "defines": "", - "vertexShader": "Shaders/example_3D_PositionTex.vsh", - "fragmentShader": "Shaders/example_3D_PositionTex.fsh", - "u_color": [1,1,1,1] - }, - "textures": [ - { - "path": "Sprite3DTest/boss.png", - "wrapS": "CLAMP_TO_EDGE", - "wrapT": "CLAMP_TO_EDGE", - "minFilter": "LINEAR", - "magFilter": "LINEAR", - "mipmap": "false" - } - ] - } - ] - } - ] -} diff --git a/tests/cpp-tests/Resources/Shaders3D/3d_color_normal_tex.frag b/tests/cpp-tests/Resources/Shaders3D/3d_color_normal_tex.frag new file mode 100644 index 0000000000..3ffac456e1 --- /dev/null +++ b/tests/cpp-tests/Resources/Shaders3D/3d_color_normal_tex.frag @@ -0,0 +1,109 @@ +#if (MAX_DIRECTIONAL_LIGHT_NUM > 0) +uniform vec3 u_DirLightSourceColor[MAX_DIRECTIONAL_LIGHT_NUM]; +uniform vec3 u_DirLightSourceDirection[MAX_DIRECTIONAL_LIGHT_NUM]; +#endif +#if (MAX_POINT_LIGHT_NUM > 0) +uniform vec3 u_PointLightSourceColor[MAX_POINT_LIGHT_NUM]; +uniform float u_PointLightSourceRangeInverse[MAX_POINT_LIGHT_NUM]; +#endif +#if (MAX_SPOT_LIGHT_NUM > 0) +uniform vec3 u_SpotLightSourceColor[MAX_SPOT_LIGHT_NUM]; +uniform vec3 u_SpotLightSourceDirection[MAX_SPOT_LIGHT_NUM]; +uniform float u_SpotLightSourceInnerAngleCos[MAX_SPOT_LIGHT_NUM]; +uniform float u_SpotLightSourceOuterAngleCos[MAX_SPOT_LIGHT_NUM]; +uniform float u_SpotLightSourceRangeInverse[MAX_SPOT_LIGHT_NUM]; +#endif +uniform vec3 u_AmbientLightSourceColor; + +#ifdef GL_ES +varying mediump vec2 TextureCoordOut; +#if MAX_POINT_LIGHT_NUM +varying mediump vec3 v_vertexToPointLightDirection[MAX_POINT_LIGHT_NUM]; +#endif +#if MAX_SPOT_LIGHT_NUM +varying mediump vec3 v_vertexToSpotLightDirection[MAX_SPOT_LIGHT_NUM]; +#endif +#if ((MAX_DIRECTIONAL_LIGHT_NUM > 0) || (MAX_POINT_LIGHT_NUM > 0) || (MAX_SPOT_LIGHT_NUM > 0)) +varying mediump vec3 v_normal; +#endif + +#else + +varying vec2 TextureCoordOut; +#if MAX_POINT_LIGHT_NUM +varying vec3 v_vertexToPointLightDirection[MAX_POINT_LIGHT_NUM]; +#endif +#if MAX_SPOT_LIGHT_NUM +varying vec3 v_vertexToSpotLightDirection[MAX_SPOT_LIGHT_NUM]; +#endif +#if ((MAX_DIRECTIONAL_LIGHT_NUM > 0) || (MAX_POINT_LIGHT_NUM > 0) || (MAX_SPOT_LIGHT_NUM > 0)) +varying vec3 v_normal; +#endif + +#endif + +uniform vec4 u_color; + +vec3 computeLighting(vec3 normalVector, vec3 lightDirection, vec3 lightColor, float attenuation) +{ + float diffuse = max(dot(normalVector, lightDirection), 0.0); + vec3 diffuseColor = lightColor * diffuse * attenuation; + + return diffuseColor; +} + +void main(void) +{ +#if ((MAX_DIRECTIONAL_LIGHT_NUM > 0) || (MAX_POINT_LIGHT_NUM > 0) || (MAX_SPOT_LIGHT_NUM > 0)) + vec3 normal = normalize(v_normal); +#endif + + vec4 combinedColor = vec4(u_AmbientLightSourceColor, 1.0); + + // Directional light contribution +#if (MAX_DIRECTIONAL_LIGHT_NUM > 0) + for (int i = 0; i < MAX_DIRECTIONAL_LIGHT_NUM; ++i) + { + vec3 lightDirection = normalize(u_DirLightSourceDirection[i] * 2.0); + combinedColor.xyz += computeLighting(normal, -lightDirection, u_DirLightSourceColor[i], 1.0); + } +#endif + + // Point light contribution +#if (MAX_POINT_LIGHT_NUM > 0) + for (int i = 0; i < MAX_POINT_LIGHT_NUM; ++i) + { + vec3 ldir = v_vertexToPointLightDirection[i] * u_PointLightSourceRangeInverse[i]; + float attenuation = clamp(1.0 - dot(ldir, ldir), 0.0, 1.0); + combinedColor.xyz += computeLighting(normal, normalize(v_vertexToPointLightDirection[i]), u_PointLightSourceColor[i], attenuation); + } +#endif + + // Spot light contribution +#if (MAX_SPOT_LIGHT_NUM > 0) + for (int i = 0; i < MAX_SPOT_LIGHT_NUM; ++i) + { + // Compute range attenuation + vec3 ldir = v_vertexToSpotLightDirection[i] * u_SpotLightSourceRangeInverse[i]; + float attenuation = clamp(1.0 - dot(ldir, ldir), 0.0, 1.0); + vec3 vertexToSpotLightDirection = normalize(v_vertexToSpotLightDirection[i]); + + vec3 spotLightDirection = normalize(u_SpotLightSourceDirection[i] * 2.0); + + // "-lightDirection" is used because light direction points in opposite direction to spot direction. + float spotCurrentAngleCos = dot(spotLightDirection, -vertexToSpotLightDirection); + + // Apply spot attenuation + attenuation *= smoothstep(u_SpotLightSourceOuterAngleCos[i], u_SpotLightSourceInnerAngleCos[i], spotCurrentAngleCos); + attenuation = clamp(attenuation, 0.0, 1.0); + combinedColor.xyz += computeLighting(normal, vertexToSpotLightDirection, u_SpotLightSourceColor[i], attenuation); + } +#endif + +#if ((MAX_DIRECTIONAL_LIGHT_NUM > 0) || (MAX_POINT_LIGHT_NUM > 0) || (MAX_SPOT_LIGHT_NUM > 0)) + gl_FragColor = texture2D(CC_Texture0, TextureCoordOut) * u_color * combinedColor; +#else + gl_FragColor = texture2D(CC_Texture0, TextureCoordOut) * u_color; +#endif + +} diff --git a/tests/cpp-tests/Resources/Shaders3D/3d_color_tex.frag b/tests/cpp-tests/Resources/Shaders3D/3d_color_tex.frag new file mode 100644 index 0000000000..0d87c7e229 --- /dev/null +++ b/tests/cpp-tests/Resources/Shaders3D/3d_color_tex.frag @@ -0,0 +1,12 @@ +#ifdef GL_ES +varying mediump vec2 TextureCoordOut; +#else +varying vec2 TextureCoordOut; +#endif +uniform vec4 u_color; + +void main(void) +{ + gl_FragColor = texture2D(CC_Texture0, TextureCoordOut) * u_color; +} + diff --git a/tests/cpp-tests/Resources/Shaders3D/3d_position_normal_tex.vert b/tests/cpp-tests/Resources/Shaders3D/3d_position_normal_tex.vert new file mode 100644 index 0000000000..ce6aa82a48 --- /dev/null +++ b/tests/cpp-tests/Resources/Shaders3D/3d_position_normal_tex.vert @@ -0,0 +1,48 @@ +#if (MAX_POINT_LIGHT_NUM > 0) +uniform vec3 u_PointLightSourcePosition[MAX_POINT_LIGHT_NUM]; +#endif +#if (MAX_SPOT_LIGHT_NUM > 0) +uniform vec3 u_SpotLightSourcePosition[MAX_SPOT_LIGHT_NUM]; +#endif + +attribute vec4 a_position; +attribute vec2 a_texCoord; +attribute vec3 a_normal; +varying vec2 TextureCoordOut; + +#if MAX_POINT_LIGHT_NUM +varying vec3 v_vertexToPointLightDirection[MAX_POINT_LIGHT_NUM]; +#endif +#if MAX_SPOT_LIGHT_NUM +varying vec3 v_vertexToSpotLightDirection[MAX_SPOT_LIGHT_NUM]; +#endif +#if ((MAX_DIRECTIONAL_LIGHT_NUM > 0) || (MAX_POINT_LIGHT_NUM > 0) || (MAX_SPOT_LIGHT_NUM > 0)) +varying vec3 v_normal; +#endif + +void main(void) +{ + vec4 ePosition = CC_MVMatrix * a_position; +#if (MAX_POINT_LIGHT_NUM > 0) + for (int i = 0; i < MAX_POINT_LIGHT_NUM; ++i) + { + v_vertexToPointLightDirection[i] = u_PointLightSourcePosition[i].xyz - ePosition.xyz; + } +#endif + +#if (MAX_SPOT_LIGHT_NUM > 0) + for (int i = 0; i < MAX_SPOT_LIGHT_NUM; ++i) + { + v_vertexToSpotLightDirection[i] = u_SpotLightSourcePosition[i] - ePosition.xyz; + } +#endif + +#if ((MAX_DIRECTIONAL_LIGHT_NUM > 0) || (MAX_POINT_LIGHT_NUM > 0) || (MAX_SPOT_LIGHT_NUM > 0)) + v_normal = CC_NormalMatrix * a_normal; +#endif + + TextureCoordOut = a_texCoord; + TextureCoordOut.y = 1.0 - TextureCoordOut.y; + gl_Position = CC_PMatrix * ePosition; +} + diff --git a/tests/cpp-tests/Resources/Shaders3D/3d_position_skin_tex.vert b/tests/cpp-tests/Resources/Shaders3D/3d_position_skin_tex.vert new file mode 100644 index 0000000000..fa72d3377d --- /dev/null +++ b/tests/cpp-tests/Resources/Shaders3D/3d_position_skin_tex.vert @@ -0,0 +1,69 @@ +attribute vec3 a_position; + +attribute vec4 a_blendWeight; +attribute vec4 a_blendIndex; + +attribute vec2 a_texCoord; + +const int SKINNING_JOINT_COUNT = 60; +// Uniforms +uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3]; + +// Varyings +varying vec2 TextureCoordOut; + +vec4 getPosition() +{ + float blendWeight = a_blendWeight[0]; + + int matrixIndex = int (a_blendIndex[0]) * 3; + vec4 matrixPalette1 = u_matrixPalette[matrixIndex] * blendWeight; + vec4 matrixPalette2 = u_matrixPalette[matrixIndex + 1] * blendWeight; + vec4 matrixPalette3 = u_matrixPalette[matrixIndex + 2] * blendWeight; + + + blendWeight = a_blendWeight[1]; + if (blendWeight > 0.0) + { + matrixIndex = int(a_blendIndex[1]) * 3; + matrixPalette1 += u_matrixPalette[matrixIndex] * blendWeight; + matrixPalette2 += u_matrixPalette[matrixIndex + 1] * blendWeight; + matrixPalette3 += u_matrixPalette[matrixIndex + 2] * blendWeight; + + blendWeight = a_blendWeight[2]; + if (blendWeight > 0.0) + { + matrixIndex = int(a_blendIndex[2]) * 3; + matrixPalette1 += u_matrixPalette[matrixIndex] * blendWeight; + matrixPalette2 += u_matrixPalette[matrixIndex + 1] * blendWeight; + matrixPalette3 += u_matrixPalette[matrixIndex + 2] * blendWeight; + + blendWeight = a_blendWeight[3]; + if (blendWeight > 0.0) + { + matrixIndex = int(a_blendIndex[3]) * 3; + matrixPalette1 += u_matrixPalette[matrixIndex] * blendWeight; + matrixPalette2 += u_matrixPalette[matrixIndex + 1] * blendWeight; + matrixPalette3 += u_matrixPalette[matrixIndex + 2] * blendWeight; + } + } + } + + vec4 _skinnedPosition; + vec4 postion = vec4(a_position, 1.0); + _skinnedPosition.x = dot(postion, matrixPalette1); + _skinnedPosition.y = dot(postion, matrixPalette2); + _skinnedPosition.z = dot(postion, matrixPalette3); + _skinnedPosition.w = postion.w; + + return _skinnedPosition; +} + +void main() +{ + vec4 position = getPosition(); + gl_Position = CC_MVPMatrix * position; + + TextureCoordOut = a_texCoord; + TextureCoordOut.y = 1.0 - TextureCoordOut.y; +} diff --git a/tests/cpp-tests/Resources/Shaders3D/3d_position_tex.vert b/tests/cpp-tests/Resources/Shaders3D/3d_position_tex.vert new file mode 100644 index 0000000000..7180be8fa8 --- /dev/null +++ b/tests/cpp-tests/Resources/Shaders3D/3d_position_tex.vert @@ -0,0 +1,11 @@ +attribute vec4 a_position; +attribute vec2 a_texCoord; + +varying vec2 TextureCoordOut; + +void main(void) +{ + gl_Position = CC_MVPMatrix * a_position; + TextureCoordOut = a_texCoord; + TextureCoordOut.y = 1.0 - TextureCoordOut.y; +}