void CC3VertexTextureCoordinates::alignWithTextureCoverage( const CCSize& texCoverage ) { CCAssert((texCoverage.width && texCoverage.height), "CC3VertexTextureCoordinates mapsize cannot have zero dimension"); // Don't waste time adjusting if nothing is changing // (eg. POT textures, or new texture has same texture map as old). if (texCoverage.equals(m_mapSize)) return; CC3_TRACE( "[vtx]CC3VertexTextureCoordinates aligning and changing map size from %s to %s but not flipping vertically", stringFromCCSize(m_mapSize).c_str(), stringFromCCSize(texCoverage).c_str() ); // The scale factor CCSize mapRatio = CCSizeMake(texCoverage.width / m_mapSize.width, texCoverage.height / m_mapSize.height); // The amount by which to translate the image vertically GLfloat currVertXln = 1.0f - m_mapSize.height; GLfloat newVertXln = 1.0f - texCoverage.height; for (GLuint i = 0; i < m_vertexCount; i++) { ccTex2F* ptc = (ccTex2F*)getAddressOfElement(i); ptc->u *= mapRatio.width; ptc->v = (ptc->v - currVertXln) * mapRatio.height + newVertXln; } m_mapSize = texCoverage; // Remember what we've set the map size to updateGLBuffer(); }
/** * If the particle system has exhausted and it is set to auto-remove, remove this * node from the scene so that this node and the particle system will be released. */ void CC3ParticleSystemBillboard::updateBeforeTransform( CC3NodeUpdatingVisitor* visitor ) { if ( m_pBillboard ) { CCParticleSystem* ps = (CCParticleSystem*)m_pBillboard; if ( ps->isAutoRemoveOnFinish() && !ps->isActive() && ps->getParticleCount() == 0 ) { CC3_TRACE("[bbd]2D particle system exhausted. Removing"); visitor->requestRemovalOf( this ); } } }
void CC3MeshParticleEmitter::transformParticles() { GLuint partCount = getParticleCount(); CC3_TRACE("CC3MeshParticleEmitter transforming %d particles", m_particleCount); for (GLuint partIdx = 0; partIdx < partCount; partIdx++) { CC3MeshParticle* mp = (CC3MeshParticle*)m_particles->objectAtIndex( partIdx ); mp->transformVertices(); } m_isParticleTransformDirty = false; }
GLenum GLMagnifyingFunctionFromETextureFilter(GLuint eTextureFilter) { switch (eTextureFilter) { case eFilter_Nearest: return GL_NEAREST; case eFilter_Linear: return GL_LINEAR; default: CC3_TRACE("Unknown ETextureFilter '%s'", NSStringFromETextureFilter(eTextureFilter).c_str()); return GL_LINEAR; } }
GLenum GLTextureWrapFromETextureWrap(GLuint eTextureWrap) { switch (eTextureWrap) { case eWrap_Clamp: return GL_CLAMP_TO_EDGE; case eWrap_Repeat: return GL_REPEAT; default: CC3_TRACE("Unknown ETextureWrap '%s'", NSStringFromETextureWrap(eTextureWrap).c_str()); return GL_REPEAT; } }
void CC3VertexTextureCoordinates::alignWithInvertedTextureCoverage( const CCSize& texCoverage ) { CCAssert((texCoverage.width && texCoverage.height), "CC3VertexTextureCoordinates mapsize %s cannot have zero dimension"/*, stringFromCCSize(texCoverage).c_str()*/); CC3_TRACE( "[vtx]CC3VertexTextureCoordinates aligning and changing map size from %s to %s and flipping vertically", stringFromCCSize(m_mapSize).c_str(), stringFromCCSize(texCoverage).c_str() ); CCSize mapRatio = CCSizeMake(texCoverage.width / m_mapSize.width, texCoverage.height / m_mapSize.height); for (GLuint i = 0; i < m_vertexCount; i++) { ccTex2F* ptc = (ccTex2F*)getAddressOfElement( i ); ptc->u *= mapRatio.width; ptc->v = texCoverage.height - (ptc->v * mapRatio.height); } // Remember that we've flipped and what we've set the map size to m_mapSize = texCoverage; m_expectsVerticallyFlippedTextures = !m_expectsVerticallyFlippedTextures; updateGLBuffer(); CC3_TRACE("[vtx]CC3VertexTextureCoordinates aligned and flipped vertically"); }
void CC3GLSLUniform::setIntVector4( const CC3IntVector4& value, GLuint index ) { CCAssert((GLint)index < _size, "CC3GLSLUniform could not set value because index %d is out of bounds"/*, index*/); switch (_type) { case GL_FLOAT: case GL_FLOAT_VEC2: case GL_FLOAT_VEC3: case GL_FLOAT_VEC4: case GL_FLOAT_MAT2: setVector4( CC3Vector4((GLfloat)value.x, (GLfloat)value.y, (GLfloat)value.z, (GLfloat)value.w), index ); return; case GL_FLOAT_MAT3: case GL_FLOAT_MAT4: CCAssert(false, "CC3GLSLUniform attempted to set scalar or vector when matrix type %s expected."/*, stringFromGLEnum(_type).c_str()*/); return; case GL_INT: case GL_BOOL: case GL_SAMPLER_2D: case GL_SAMPLER_CUBE: ((GLint*)m_varValue)[index] = *(GLint*)&value; return; case GL_INT_VEC2: case GL_BOOL_VEC2: ((CC3IntPoint*)m_varValue)[index] = *(CC3IntPoint*)&value; return; case GL_INT_VEC3: case GL_BOOL_VEC3: ((CC3IntVector*)m_varValue)[index] = *(CC3IntVector*)&value; return; case GL_INT_VEC4: case GL_BOOL_VEC4: ((CC3IntVector4*)m_varValue)[index] = value; return; default: CCAssert(false, "CC3GLSLUniform could not set value because type %s is not understood"/*, stringFromGLEnum(_type).c_str()*/); return; } CC3_TRACE("CC3GLSLUniform setting value to (%d, %d, %d, %d)", value.x, value.y, value.z, value.w); }
void CC3Cache::addObject( CC3Cacheable* obj ) { if ( !obj ) return; std::string objName = obj->getName(); CCAssert(!objName.empty(), "obj cannot be added to the cache because its name property is nil."); if ( getObjectNamed( objName ) ) return; CC3Cacheable* wrap = obj; _objectsByName->setObject( wrap, objName ); CC3_TRACE("[rez]Added obj[%s] to the %s cache.", objName.c_str(), _typeName.c_str()); }
/** Update the particles after the transform, and then update the mesh node. */ void CC3ParticleEmitter::processUpdateAfterTransform( CC3NodeUpdatingVisitor* visitor ) { // If configured to update particles after the node is transformed, do so here. // For each particle, invoke the updateBeforeTransform: method. // Particles can also be removed during the update process. if ( m_shouldUpdateParticlesAfterTransform ) updateParticlesAfterTransform( visitor ); // If emission has stopped and all the particles have been killed off and the // emitter should be removed when finished, remove the emitter from its parent. if ( isFinished() && shouldRemoveOnFinish() ) { CC3_TRACE("[ptc]CC3ParticleEmitter is exhausted and is being removed"); visitor->requestRemovalOf( this ); } super::processUpdateAfterTransform( visitor ); }
void CC3MeshParticleEmitter::setParticleTemplateMesh( CC3Mesh* aMesh ) { if (aMesh == m_pParticleTemplateMesh) return; CC_SAFE_RELEASE( m_pParticleTemplateMesh ); m_pParticleTemplateMesh = aMesh; CC_SAFE_RETAIN( aMesh ); // Add vertex content if not already set, and align the drawing mode if ( getVertexContentTypes() == kCC3VertexContentNone ) setVertexContentTypes( aMesh->getVertexContentTypes() ); setDrawingMode( aMesh->getDrawingMode() ); CC3_TRACE( "[ptc]Particle template mesh of CC3MeshParticleEmitter set to %s drawing %s with %d vertices and %d vertex indices", aMesh->fullDescription().c_str(), stringFromGLEnum(getDrawingMode()).c_str(), aMesh->getVertexCount(), aMesh->getVertexIndexCount() ); }
/** * For each particle, invoke the updateAfterTransform: method. * If the particle has expired, remove it from the particles array. */ void CC3ParticleEmitter::updateParticlesAfterTransform( CC3NodeUpdatingVisitor* visitor ) { GLuint i = 0; while ( i < m_particleCount ) { CC3Particle* p = (CC3Particle*)m_particles->objectAtIndex( i ); p->updateAfterTransform( visitor ); if (p->isAlive()) { i++; // Move on to next particle } else { // Remove the particle from active use and don't increment iterator. CC3_TRACE("[ptc]Expiring %s", p->fullDescription().c_str()); finalizeAndRemoveParticle( p, i ); } } }
/** * Returns a 4D directional vector which can be added to each vertex when creating * the shadow volume vertices from the corresponding shadow caster vertices. * * The returned vector is in the local coordinate system of the shadow caster. * * The returned directional vector is a small offset vector in the direction away * from the light. A unit vector in that direction is scaled by both the distance * from the center of the shadow casting node to the camera and the * shadowVolumeVertexOffsetFactor property. Hence, if the shadow caster is farther * away from the camera, the returned value will be larger, to reduce the chance * of Z-fighting between the faces of the shadow volume and the shadow caster. */ CC3Vector4 CC3ShadowVolumeMeshNode::getShadowVolumeVertexOffsetForLightAt( const CC3Vector4& localLightPos ) { CC3Vector scLoc = getShadowCaster()->getLocalContentCenterOfGeometry(); CC3Vector lgtLoc = localLightPos.cc3Vector(); CC3Vector camLoc = getShadowCaster()->getGlobalTransformMatrixInverted()->transformLocation( getActiveCamera()->getGlobalLocation() ); // Get a unit offset vector in the direction away from the light CC3Vector offsetDir = (_light->isDirectionalOnly() ? lgtLoc.negate() : scLoc.difference( lgtLoc )).normalize(); // Get the distance from the shadow caster CoG and the camera, and scale the // unit offset vector by that distance and the shadowVolumeVertexOffsetFactor GLfloat camDist = scLoc.distance( camLoc ); CC3Vector offset = offsetDir.scaleUniform( camDist * _shadowVolumeVertexOffsetFactor ); CC3_TRACE("CC3ShadowVolumeMeshNode nudging vertices by %s", offset.stringfy().c_str()); // Create and return a 4D directional vector from the offset return CC3Vector4().fromDirection(offset); }
void CC3Cache::addObject( CC3Cacheable* obj ) { if ( !obj ) return; std::string objName = obj->getName(); CCAssert(!objName.empty(), "obj cannot be added to the cache because its name property is nil."); CC3Cacheable* cached = getObjectNamed( objName ); if ( cached == obj ) return; if ( cached != NULL ) { CC3_WARNING( "Duplicated objects 0x%04x and 0x%04x with the same name %s found", (unsigned long long)obj, (unsigned long long)cached, objName.c_str() ); } m_objectsByName->setObject( obj, objName ); CC3_TRACE("[rez]Added obj[%s] to the %s cache.", objName.c_str(), m_typeName.c_str()); }
bool CC3Resource::loadFromFile( const std::string& filePath ) { if (m_wasLoaded) { CC3_TRACE("[rez]CC3Resource[%s] has already been loaded.", filePath.c_str()); return m_wasLoaded; } // Resolve an absolute path in either the application bundle resource // directory or the Cocos3D bundle resource directory. std::string absFilePath = filePath; if ( absFilePath.empty() ) { CC3_TRACE( "[rez]Could not locate resource file '%s' in either the application resources or the Cocos3D library resources", filePath.c_str() ); return false; } CC3_TRACE("[rez]--------------------------------------------------"); CC3_TRACE("[rez]Loading resource from file '%s'", absFilePath.c_str()); if ( m_sName.c_str() ) setName( resourceNameFromFilePath( absFilePath ) ); if ( m_directory.empty() ) { std::string sDir = CC3String::getDirectory( absFilePath ); setDirectory( sDir ); } m_wasLoaded = processFile( absFilePath ); // Main subclass loading method if (!m_wasLoaded) { CC3_TRACE("[rez]Could not load resource file '%s'", absFilePath.c_str()); } CC3_TRACE(""); // Empty line to separate from next logs return m_wasLoaded; }
void CC3MeshParticleEmitter::removeParticle( CC3Particle* aParticle, GLuint anIndex ) { super::removeParticle( aParticle, anIndex ); // Decrements particleCount and vertexCount GLuint partCount = getParticleCount(); // Get the decremented particleCount // Particle being removed CC3MeshParticle* deadParticle = (CC3MeshParticle*)aParticle; GLuint deadFirstVtx = deadParticle->getFirstVertexOffset(); GLuint deadVtxCount = deadParticle->getVertexCount(); GLuint deadFirstVtxIdx = deadParticle->getFirstVertexIndexOffset(); GLuint deadVtxIdxCount = deadParticle->getVertexIndexCount(); // Last living particle CC3MeshParticle* lastParticle = getMeshParticleAt( partCount ); GLuint lastFirstVtx = lastParticle->getFirstVertexOffset(); GLuint lastVtxCount = lastParticle->getVertexCount(); GLuint lastFirstVtxIdx = lastParticle->getFirstVertexIndexOffset(); GLuint lastVtxIdxCount = lastParticle->getVertexIndexCount(); // Remove the template mesh from the particle, even if the particle will be reused. // This gives the emitter a chance to use a different template mesh when it reuses the particle. // Clear it before removing the particle, because the particle may disappear when removed from // this emitter. First, take note of whether the last particle has the same template mesh as the // last particle. This knowledge is used below when copying vertex indices. bool isSameTemplateMesh = (deadParticle->getTemplateMesh() == lastParticle->getTemplateMesh()); deadParticle->setTemplateMesh( NULL ); if (anIndex >= partCount) { CC3_TRACE("[ptc]Removing particle at %d by doing nothing, since particle count is now %d.", anIndex, partCount); } else if (deadVtxCount == lastVtxCount && deadVtxIdxCount == lastVtxIdxCount) { // If the two particles have the same number of vertices and vertex indices, we can swap them. CC3_TRACE("[ptc]Removing particle at %d by swapping particles of identical size.", anIndex); // Move the last living particle into the slot that is being vacated m_particles->exchangeObjectAtIndex( anIndex, partCount ); // Swap the vertex offsets of the two particles deadParticle->setFirstVertexOffset( lastFirstVtx ); deadParticle->setFirstVertexIndexOffset( lastFirstVtxIdx ); lastParticle->setFirstVertexOffset( deadFirstVtx ); lastParticle->setFirstVertexIndexOffset( deadFirstVtxIdx ); // Update the underlying mesh vertex content and mark the updated vertex dirty getMesh()->copyVertices( deadVtxCount, lastFirstVtx, deadFirstVtx ); addDirtyVertexRange( deadParticle->getVertexRange() ); // If the template meshes are the same, we don't need to update the vertex indices. if ( !isSameTemplateMesh ) { getMesh()->getVertexIndices()->copyVertices( lastVtxIdxCount, lastFirstVtxIdx, deadFirstVtxIdx, (deadFirstVtx - lastFirstVtx) ); addDirtyVertexIndexRange( deadParticle->getVertexIndexRange() ); } } else { CC3_TRACE("[ptc]Removing particle at %d by removing particle with %d vertices from collection.", anIndex, deadVtxCount); // Move the vertices in the mesh to fill the gap created by the removed particle GLuint srcVtxStart = (deadFirstVtx + deadVtxCount); // Start after removed particle GLuint srcVtxEnd = (lastFirstVtx + lastVtxCount); // End after last living particle GLuint vtxCount = srcVtxEnd - srcVtxStart; GLuint dstVtxStart = deadFirstVtx; getMesh()->copyVertices( vtxCount, srcVtxStart, dstVtxStart ); addDirtyVertexRange( CCRangeMake(dstVtxStart, vtxCount) ); // If the mesh has vertex indices, move them to fill the gap created by the removed particle // and adjust their values to fill the gap created in the vertex content. GLuint srcVtxIdxStart = (deadFirstVtxIdx + deadVtxIdxCount); // Start after removed particle GLuint srcVtxIdxEnd = (lastFirstVtxIdx + lastVtxIdxCount); // End after last living particle GLuint vtxIdxCount = srcVtxIdxEnd - srcVtxIdxStart; GLuint dstVtxIdxStart = deadFirstVtxIdx; getMesh()->copyVertexIndices( vtxIdxCount, srcVtxIdxStart, dstVtxIdxStart, -(GLint)deadVtxCount ); addDirtyVertexIndexRange( CCRangeMake(dstVtxIdxStart, vtxIdxCount) ); // Remove the particle from particles collection, // Do this last in case the particle is only being held by this collection. m_particles->removeObjectAtIndex( anIndex ); // Adjust the firstVertexOffset and firstVertexIndexOffset properties of each remaining // particle to fill in the gap created by removing the particle from the mesh arrays. // Do this after the dead particle has been removed from the collection. for (GLuint partIdx = anIndex; partIdx < partCount; partIdx++) { CC3MeshParticle* mp = getMeshParticleAt( partIdx ); GLuint firstVertexOffset = mp->getFirstVertexOffset(); GLuint firstVertexIndexOffset = mp->getFirstVertexIndexOffset(); firstVertexOffset -= deadVtxCount; firstVertexIndexOffset -= deadVtxIdxCount; mp->setFirstVertexOffset( firstVertexOffset ); mp->setFirstVertexIndexOffset( firstVertexIndexOffset ); } } }