1)_director->getTextureCache()->addImage(filename):TextureCache用于管理texture的加载(另开一个线程) 2)rect.size = texture->getContentSize():用texture content size(像素为单位,window.size也是以像素为单位)作为Sprite的size,如果是一张1024X768的图片,则rect = (0,0,1024,768),这个rect会用来生成Sprite的顶点坐标,比如这个Rect的右上角对应的顶点坐标是(1024 / window.size.w, 768 / window.size.h)
bool Node::init(){ return true;}bool Sprite::initWithTexture(Texture2D *texture, const Rect& rect, bool rotated){ bool result = false; if (Node::init()) { _batchNode = nullptr; _recursiveDirty = false; setDirty(false); _opacityModifyRGB = true; _blendFunc = BlendFunc::ALPHA_PREMULTipLIED; _flippedX = _flippedY = false; // default transform anchor: center setAnchorPoint(Vec2(0.5f, 0.5f)); // zwoptex default values _offsetPosition.setZero(); // clean the Quad memset(&_quad, 0, sizeof(_quad)); // Atlas: Color _quad.bl.colors = Color4B::WHITE; _quad.br.colors = Color4B::WHITE; _quad.tl.colors = Color4B::WHITE; _quad.tr.colors = Color4B::WHITE; // update texture (calls updateBlendFunc), set program but not set vertex attrib and uniform setTexture(texture); // set _quad, _texcoord setTextureRect(rect, rotated, rect.size); // by default use "Self Render". // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" setBatchNode(nullptr); result = true; } _recursiveDirty = true; setDirty(true); return result;}0)_quad.bl.colors = Color4B::WHITE:默认4个角的颜色为白色 1)_flippedX = _flippedY = false:不需要flip,因为TexCoords按照未flip生成的(y轴向下,OpenGL坐标系y轴向上) 2)setAnchorPoint(Vec2(0.5f, 0.5f)):除了layer类,其他Node类锚点默认在正中间 3)_offsetPosition.setZero():Node的content size可能和texture size不一样,这样就会产生offset,一般offset都是0。因为使用texture的content size来设置Node的content size的,两者相等,所以没有offset。 4)setTexture(texture):设置texture,获取GLProgramState,加载编译所有的program,如果texture为nullptr,用一张白色的图片作为texture
void Sprite::setTexture(Texture2D *texture){ setGLProgramState(GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, texture)); // If batchnode, then texture id should be the same CCASSERT(! _batchNode || (texture && texture->getName() == _batchNode->getTexture()->getName()), "CCSprite: Batched sprites should use the same texture as the batchnode"); // accept texture==nil as argument CCASSERT( !texture || dynamic_cast<Texture2D*>(texture), "setTexture expects a Texture2D. Invalid argument"); if (texture == nullptr) { // Gets the texture by key firstly. texture = _director->getTextureCache()->getTextureForKey(CC_2x2_WHITE_IMAGE_KEY); // If texture wasn't in cache, create it from RAW data. if (texture == nullptr) { Image* image = new (std::nothrow) Image(); bool isOK = image->initWithRawData(cc_2x2_white_image, sizeof(cc_2x2_white_image), 2, 2, 8); CC_UNUSED_PARAM(isOK); CCASSERT(isOK, "The 2x2 empty texture was created unsuccessfully."); texture = _director->getTextureCache()->addImage(image, CC_2x2_WHITE_IMAGE_KEY); CC_SAFE_RELEASE(image); } } if (!_batchNode && _texture != texture) { CC_SAFE_RETAIN(texture); CC_SAFE_RELEASE(_texture); _texture = texture; updateBlendFunc(); }}5)setTextureRect(rect, rotated, rect.size):setTexCoords(左上为(0,0), 左下为(0.1),load image不flip,TexCoords实现flip)和setPosition(position没有变,左下为(0,0))
void Sprite::setTextureRect(const Rect& rect, bool rotated, const Size& untrimmedSize){ _rectRotated = rotated; setContentSize(untrimmedSize); setVertexRect(rect); setTextureCoords(rect); float relativeOffsetX = _unflippedOffsetPositionFromCenter.x; float relativeOffsetY = _unflippedOffsetPositionFromCenter.y; // issue #732 if (_flippedX) { relativeOffsetX = -relativeOffsetX; } if (_flippedY) { relativeOffsetY = -relativeOffsetY; } _offsetPosition.x = relativeOffsetX + (_contentSize.width - _rect.size.width) / 2; _offsetPosition.y = relativeOffsetY + (_contentSize.height - _rect.size.height) / 2; // rendering using batch node if (_batchNode) { // update dirty_, don't update recursiveDirty_ setDirty(true); } else { // self rendering // Atlas: Vertex float x1 = 0.0f + _offsetPosition.x; float y1 = 0.0f + _offsetPosition.y; float x2 = x1 + _rect.size.width; float y2 = y1 + _rect.size.height; // Don't update Z. _quad.bl.vertices.set(x1, y1, 0.0f); _quad.br.vertices.set(x2, y1, 0.0f); _quad.tl.vertices.set(x1, y2, 0.0f); _quad.tr.vertices.set(x2, y2, 0.0f); } _polyInfo.setQuad(&_quad);}void Node::setContentSize(const Size & size){ if (! size.equals(_contentSize)) { _contentSize = size; _anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y); _transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true; }}void Sprite::setVertexRect(const Rect& rect){ _rect = rect;}void Sprite::setTextureCoords(const Rect& rectInPoint){ Texture2D *tex = _batchNode ? _textureAtlas->getTexture() : _texture; if (tex == nullptr) { return; } auto rectInPixels = CC_RECT_POINTS_TO_PIXELS(rectInPoint); float atlasWidth = (float)tex->getPixelsWide(); float atlasHeight = (float)tex->getPixelsHigh(); float left, right, top, bottom; if (_rectRotated) {#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL left = (2*rectInPixels.origin.x+1)/(2*atlasWidth); right = left+(rectInPixels.size.height*2-2)/(2*atlasWidth); top = (2*rectInPixels.origin.y+1)/(2*atlasHeight); bottom = top+(rectInPixels.size.width*2-2)/(2*atlasHeight);#else left = rectInPixels.origin.x/atlasWidth; right = (rectInPixels.origin.x+rectInPixels.size.height) / atlasWidth; top = rectInPixels.origin.y/atlasHeight; bottom = (rectInPixels.origin.y+rectInPixels.size.width) / atlasHeight;#endif // CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL if (_flippedX) { std::swap(top, bottom); } if (_flippedY) { std::swap(left, right); } _quad.bl.texCoords.u = left; _quad.bl.texCoords.v = top; _quad.br.texCoords.u = left; _quad.br.texCoords.v = bottom; _quad.tl.texCoords.u = right; _quad.tl.texCoords.v = top; _quad.tr.texCoords.u = right; _quad.tr.texCoords.v = bottom; } else {#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL left = (2*rectInPixels.origin.x+1)/(2*atlasWidth); right = left + (rectInPixels.size.width*2-2)/(2*atlasWidth); top = (2*rectInPixels.origin.y+1)/(2*atlasHeight); bottom = top + (rectInPixels.size.height*2-2)/(2*atlasHeight);#else left = rectInPixels.origin.x/atlasWidth; right = (rectInPixels.origin.x + rectInPixels.size.width) / atlasWidth; top = rectInPixels.origin.y/atlasHeight; bottom = (rectInPixels.origin.y + rectInPixels.size.height) / atlasHeight;#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL if(_flippedX) { std::swap(left, right); } if(_flippedY) { std::swap(top, bottom); } _quad.bl.texCoords.u = left; _quad.bl.texCoords.v = bottom; _quad.br.texCoords.u = right; _quad.br.texCoords.v = bottom; _quad.tl.texCoords.u = left; _quad.tl.texCoords.v = top; _quad.tr.texCoords.u = right; _quad.tr.texCoords.v = top; }}/**The structure of Triangles. */struct Triangles{ /**Vertex data pointer.*/ V3F_C4B_T2F* verts; /**Index data pointer.*/ unsigned short* indices; /**The number of vertices.*/ int vertCount; /**The number of indices.*/ int indexCount;};static unsigned short quadIndices[]={0,1,2, 3,2,1};void PolygonInfo::setQuad(V3F_C4B_T2F_Quad *quad){ releaseVertsAndIndices(); isVertsOwner = false; triangles.indices = quadIndices; triangles.vertCount = 4; triangles.indexCount = 6; triangles.verts = (V3F_C4B_T2F*)quad;}6)init之后,texture,position(未归一化,bl(0,0),br(0,1024),tl(0,768),tr(1024,768)),indices(预定义的{0,1,2, 3,2,1}),TexCoords(flip过的,bl(0,1),br(1,1),tl(0,0),tr(1,0))
1)这里的Position是用来生成translate的 2)与移动相关的Action修改的也是这个Position 3)如何由Position到Transform Matrix
const Mat4& Node::getNodeToParentTransform() const{ if (_transformDirty) { // Translate values 0 float x = _position.x; float y = _position.y; float z = _positionZ; // _ignoreAnchorPointForPosition = false if (_ignoreAnchorPointForPosition) { x += _anchorPointInPoints.x; y += _anchorPointInPoints.y; } bool needsSkewMatrix = ( _skewX || _skewY ); Vec2 anchorPoint(_anchorPointInPoints.x * _scaleX, _anchorPointInPoints.y * _scaleY); // calculate real position 1 // get real translation if (! needsSkewMatrix && !_anchorPointInPoints.isZero()) { x += -anchorPoint.x; y += -anchorPoint.y; } // Build Transform Matrix = translation * rotation * scale Mat4 translation; // move to anchor point first, then rotate 2 // will restore after rotate Mat4::createTranslation(x + anchorPoint.x, y + anchorPoint.y, z, &translation); Mat4::createRotation(_rotationQuat, &_transform); if (_rotationZ_X != _rotationZ_Y) { // Rotation values // Change rotation code to handle X and Y // If we skew with the exact same value for both x and y then we're simply just rotating float radiansX = -CC_DEGREES_TO_RADIANS(_rotationZ_X); float radiansY = -CC_DEGREES_TO_RADIANS(_rotationZ_Y); float cx = cosf(radiansX); float sx = sinf(radiansX); float cy = cosf(radiansY); float sy = sinf(radiansY); float m0 = _transform.m[0], m1 = _transform.m[1], m4 = _transform.m[4], m5 = _transform.m[5], m8 = _transform.m[8], m9 = _transform.m[9]; _transform.m[0] = cy * m0 - sx * m1, _transform.m[4] = cy * m4 - sx * m5, _transform.m[8] = cy * m8 - sx * m9; _transform.m[1] = sy * m0 + cx * m1, _transform.m[5] = sy * m4 + cx * m5, _transform.m[9] = sy * m8 + cx * m9; } _transform = translation * _transform; // move by (-anchorPoint.x, -anchorPoint.y, 0) after rotation 3 // restore position after rotate _transform.translate(-anchorPoint.x, -anchorPoint.y, 0); if (_scaleX != 1.f) { _transform.m[0] *= _scaleX, _transform.m[1] *= _scaleX, _transform.m[2] *= _scaleX; } if (_scaleY != 1.f) { _transform.m[4] *= _scaleY, _transform.m[5] *= _scaleY, _transform.m[6] *= _scaleY; } if (_scaleZ != 1.f) { _transform.m[8] *= _scaleZ, _transform.m[9] *= _scaleZ, _transform.m[10] *= _scaleZ; } // FIXME:: Try to inline skew // If skew is needed, apply skew and then anchor point if (needsSkewMatrix) { float skewMatArray[16] = { 1, (float)tanf(CC_DEGREES_TO_RADIANS(_skewY)), 0, 0, (float)tanf(CC_DEGREES_TO_RADIANS(_skewX)), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; Mat4 skewMatrix(skewMatArray); _transform = _transform * skewMatrix; // adjust anchor point if (!_anchorPointInPoints.isZero()) { // FIXME:: Argh, Mat4 needs a "translate" method. // FIXME:: Although this is faster than multiplying a vec4 * mat4 _transform.m[12] += _transform.m[0] * -_anchorPointInPoints.x + _transform.m[4] * -_anchorPointInPoints.y; _transform.m[13] += _transform.m[1] * -_anchorPointInPoints.x + _transform.m[5] * -_anchorPointInPoints.y; } } } // only camera node need this maybe, get additional transform from scene's eyeTransform Matrix if (_additionalTransform) { // This is needed to support both Node::setNodeToParentTransform() and Node::setAdditionalTransform() // at the same time. The scenario is this: // at some point setNodeToParentTransform() is called. // and later setAdditionalTransform() is called every time. And since _transform // is being overwritten everyframe, _additionalTransform[1] is used to have a copy // of the last "_transform without _additionalTransform" if (_transformDirty) _additionalTransform[1] = _transform; if (_transformUpdated) _transform = _additionalTransform[1] * _additionalTransform[0]; } _transformDirty = _additionalTransformDirty = false; return _transform;}由Position get _modelViewTransform
uint32_t Node::processParentFlags(const Mat4& parentTransform, uint32_t parentFlags){ // _usingNormalizedPosition = false if(_usingNormalizedPosition) { CCASSERT(_parent, "setNormalizedPosition() doesn't work with orphan nodes"); if ((parentFlags & FLAGS_CONTENT_SIZE_DIRTY) || _normalizedPositionDirty) { auto& s = _parent->getContentSize(); _position.x = _normalizedPosition.x * s.width; _position.y = _normalizedPosition.y * s.height; _transformUpdated = _transformDirty = _inverseDirty = true; _normalizedPositionDirty = false; } } // Fixes Github issue #16100. Basically when having two cameras, one camera might set as dirty the // node that is not visited by it, and might affect certain calculations. Besides, it is faster to do this. if (!isVisitableByVisitingCamera()) return parentFlags; uint32_t flags = parentFlags; flags |= (_transformUpdated ? FLAGS_TRANSFORM_DIRTY : 0); flags |= (_contentSizeDirty ? FLAGS_CONTENT_SIZE_DIRTY : 0); // true if(flags & FLAGS_DIRTY_MASK) _modelViewTransform = this->transform(parentTransform); _transformUpdated = false; _contentSizeDirty = false; return flags;}Mat4 Node::transform(const Mat4& parentTransform){ return parentTransform * this->getNodeToParentTransform();}const Mat4& Node::getNodeToParentTransform() const{ if (_transformDirty) { // Translate values float x = _position.x; float y = _position.y; float z = _positionZ; // false if (_ignoreAnchorPointForPosition) { x += _anchorPointInPoints.x; y += _anchorPointInPoints.y; } bool needsSkewMatrix = ( _skewX || _skewY ); // (1024.0/2, 768.0/2) Vec2 anchorPoint(_anchorPointInPoints.x * _scaleX, _anchorPointInPoints.y * _scaleY); // calculate real position // not ignore AnchorPoint, calculate real translate values if (! needsSkewMatrix && !_anchorPointInPoints.isZero()) { x += -anchorPoint.x; y += -anchorPoint.y; } // Build Transform Matrix = translation * rotation * scale Mat4 translation; //move to anchor point first, then rotate 2 Mat4::createTranslation(x + anchorPoint.x, y + anchorPoint.y, z, &translation); Mat4::createRotation(_rotationQuat, &_transform); if (_rotationZ_X != _rotationZ_Y) { // Rotation values // Change rotation code to handle X and Y // If we skew with the exact same value for both x and y then we're simply just rotating float radiansX = -CC_DEGREES_TO_RADIANS(_rotationZ_X); float radiansY = -CC_DEGREES_TO_RADIANS(_rotationZ_Y); float cx = cosf(radiansX); float sx = sinf(radiansX); float cy = cosf(radiansY); float sy = sinf(radiansY); float m0 = _transform.m[0], m1 = _transform.m[1], m4 = _transform.m[4], m5 = _transform.m[5], m8 = _transform.m[8], m9 = _transform.m[9]; _transform.m[0] = cy * m0 - sx * m1, _transform.m[4] = cy * m4 - sx * m5, _transform.m[8] = cy * m8 - sx * m9; _transform.m[1] = sy * m0 + cx * m1, _transform.m[5] = sy * m4 + cx * m5, _transform.m[9] = sy * m8 + cx * m9; } _transform = translation * _transform; //move by (-anchorPoint.x, -anchorPoint.y, 0) after rotation 3 _transform.translate(-anchorPoint.x, -anchorPoint.y, 0); if (_scaleX != 1.f) { _transform.m[0] *= _scaleX, _transform.m[1] *= _scaleX, _transform.m[2] *= _scaleX; } if (_scaleY != 1.f) { _transform.m[4] *= _scaleY, _transform.m[5] *= _scaleY, _transform.m[6] *= _scaleY; } if (_scaleZ != 1.f) { _transform.m[8] *= _scaleZ, _transform.m[9] *= _scaleZ, _transform.m[10] *= _scaleZ; } // FIXME:: Try to inline skew // If skew is needed, apply skew and then anchor point if (needsSkewMatrix) { float skewMatArray[16] = { 1, (float)tanf(CC_DEGREES_TO_RADIANS(_skewY)), 0, 0, (float)tanf(CC_DEGREES_TO_RADIANS(_skewX)), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; Mat4 skewMatrix(skewMatArray); _transform = _transform * skewMatrix; // adjust anchor point if (!_anchorPointInPoints.isZero()) { // FIXME:: Argh, Mat4 needs a "translate" method. // FIXME:: Although this is faster than multiplying a vec4 * mat4 _transform.m[12] += _transform.m[0] * -_anchorPointInPoints.x + _transform.m[4] * -_anchorPointInPoints.y; _transform.m[13] += _transform.m[1] * -_anchorPointInPoints.x + _transform.m[5] * -_anchorPointInPoints.y; } } } // false if (_additionalTransform) { // This is needed to support both Node::setNodeToParentTransform() and Node::setAdditionalTransform() // at the same time. The scenario is this: // at some point setNodeToParentTransform() is called. // and later setAdditionalTransform() is called every time. And since _transform // is being overwritten everyframe, _additionalTransform[1] is used to have a copy // of the last "_transform without _additionalTransform" if (_transformDirty) _additionalTransform[1] = _transform; if (_transformUpdated) _transform = _additionalTransform[1] * _additionalTransform[0]; } _transformDirty = _additionalTransformDirty = false; return _transform;}visit就是沿着Node树从root node把transform传递下去,如:scene的translate同样要apply到子children node,并且在apply transform后,按照树关系调用children node的draw()函数add command,Sprite添加TrianglesCommand
缺陷: 1)每次render都会重新更新VBO中的vertex data,可能这样比较适合 2)glVertexAttribPointer和GLProgramState分离,GLProgramState并不起作用
新闻热点
疑难解答