首页 > 学院 > 开发设计 > 正文

Cocos2d-x Sprite笔记

2019-11-07 23:36:42
字体:
来源:转载
供稿:网友

1.create

auto sPRite = Sprite::create("HelloWorld.png");Sprite* Sprite::create(const std::string& filename){ Sprite *sprite = new (std::nothrow) Sprite(); if (sprite && sprite->initWithFile(filename)) { sprite->autorelease(); return sprite; } CC_SAFE_DELETE(sprite); return nullptr;}Sprite::Sprite(void): _batchNode(nullptr), _textureAtlas(nullptr), _shouldBeHidden(false), _texture(nullptr), _spriteFrame(nullptr), _insideBounds(true){}

2.init

bool Sprite::initWithFile(const std::string& filename){ if (filename.empty()) { CCLOG("Call Sprite::initWithFile with blank resource filename."); return false; } _fileName = filename; _fileType = 0; Texture2D *texture = _director->getTextureCache()->addImage(filename); if (texture) { Rect rect = Rect::ZERO; rect.size = texture->getContentSize(); return initWithTexture(texture, rect); } // don't release here. // when load texture failed, it's better to get a "transparent" sprite then a crashed program // this->release(); return false;}

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))

3.setPosition

sprite->setPosition(Vec2(visibleSize / 2) + origin);void Sprite::setPosition(const Vec2& pos){ Node::setPosition(pos); SET_DIRTY_RECURSIVELY();}void Node::setPosition(float x, float y){ if (_position.x == x && _position.y == y) return; _position.x = x; _position.y = y; _transformUpdated = _transformDirty = _inverseDirty = true; _usingNormalizedPosition = false;}

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;}

4.visit

void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags){ // quick return if not visible. children won't be drawn. if (!_visible) { return; } // set transform uint32_t flags = processParentFlags(parentTransform, parentFlags); // IMPORTANT: // To ease the migration to v3.0, we still support the Mat4 stack, // but it is deprecated and your code should not rely on it _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform); bool visibleByCamera = isVisitableByVisitingCamera(); int i = 0; if(!_children.empty()) { sortAllChildren(); // draw children zOrder < 0 for( ; i < _children.size(); i++ ) { auto node = _children.at(i); if (node && node->_localZOrder < 0) node->visit(renderer, _modelViewTransform, flags); else break; } // self draw if (visibleByCamera) this->draw(renderer, _modelViewTransform, flags); for(auto it=_children.cbegin()+i; it != _children.cend(); ++it) (*it)->visit(renderer, _modelViewTransform, flags); } else if (visibleByCamera) { this->draw(renderer, _modelViewTransform, flags); } _director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); // FIX ME: Why need to set _orderOfArrival to 0?? // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920 // reset for next frame // _orderOfArrival = 0;}

由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

5.draw

void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags){ if (_texture == nullptr) { return; }#if CC_USE_CULLING // Don't calculate the culling if the transform was not updated auto visitingCamera = Camera::getVisitingCamera(); auto defaultCamera = Camera::getDefaultCamera(); if (visitingCamera == defaultCamera) { _insideBounds = ((flags & FLAGS_TRANSFORM_DIRTY) || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds; } else { // XXX: this always return true since _insideBounds = renderer->checkVisibility(transform, _contentSize); } if(_insideBounds)#endif { _trianglesCommand.init(_globalZOrder, _texture, getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags); renderer->addCommand(&_trianglesCommand); }}void TrianglesCommand::init(float globalOrder, GLuint textureID, GLProgramState* glProgramState, BlendFunc blendType, const Triangles& triangles,const Mat4& mv, uint32_t flags){ CCASSERT(glProgramState, "Invalid GLProgramState"); CCASSERT(glProgramState->getVertexAttribsFlags() == 0, "No custom attributes are supported in QuadCommand"); RenderCommand::init(globalOrder, mv, flags); _triangles = triangles; if(_triangles.indexCount % 3 != 0) { int count = _triangles.indexCount; _triangles.indexCount = count / 3 * 3; CCLOGERROR("Resize indexCount from %zd to %zd, size must be multiple times of 3", count, _triangles.indexCount); } _mv = mv; if( _textureID != textureID || _blendType.src != blendType.src || _blendType.dst != blendType.dst || _glProgramState != glProgramState || _glProgram != glProgramState->getGLProgram()) { _textureID = textureID; _blendType = blendType; _glProgramState = glProgramState; _glProgram = glProgramState->getGLProgram(); generateMaterialID(); }}void Renderer::addCommand(RenderCommand* command){ // renderQueue = 0, usual only one renderGroup int renderQueue =_commandGroupStack.top(); addCommand(command, renderQueue);}void Renderer::addCommand(RenderCommand* command, int renderQueue){ CCASSERT(!_isRendering, "Cannot add command while rendering"); CCASSERT(renderQueue >=0, "Invalid render queue"); CCASSERT(command->getType() != RenderCommand::Type::UNKNOWN_COMMAND, "Invalid Command Type"); // only have one render groups, but five commands array : GLOBALZ_NEG, OPAQUE_3D ... _renderGroups[renderQueue].push_back(command);}enum QUEUE_GROUP{ /**Objects with globalZ smaller than 0.*/ GLOBALZ_NEG = 0, /**Opaque 3D objects with 0 globalZ.*/ OPAQUE_3D = 1, /**Transparent 3D objects with 0 globalZ.*/ TRANSPARENT_3D = 2, /**2D objects with 0 globalZ.*/ GLOBALZ_ZERO = 3, /**Objects with globalZ bigger than 0.*/ GLOBALZ_POS = 4, /**Max Queue Count*/ QUEUE_COUNT = 5,};void RenderQueue::push_back(RenderCommand* command){ float z = command->getGlobalOrder(); if(z < 0) { _commands[QUEUE_GROUP::GLOBALZ_NEG].push_back(command); } else if(z > 0) { _commands[QUEUE_GROUP::GLOBALZ_POS].push_back(command); } else { if(command->is3D()) { if(command->isTransparent()) { _commands[QUEUE_GROUP::TRANSPARENT_3D].push_back(command); } else { _commands[QUEUE_GROUP::OPAQUE_3D].push_back(command); } } else { _commands[QUEUE_GROUP::GLOBALZ_ZERO].push_back(command); } }}

6.render

void Renderer::render(){ //Uncomment this once everything is rendered by new renderer //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //TODO: setup camera or MVP _isRendering = true; if (_glViewAssigned) { //Process render commands //1. Sort render commands based on ID for (auto &renderqueue : _renderGroups) { // sort use zorder renderqueue.sort(); } // visitRenderQueue(_renderGroups[0]); } clean(); _isRendering = false;}void RenderQueue::sort(){ // Don't sort _queue0, it already comes sorted std::sort(std::begin(_commands[QUEUE_GROUP::TRANSPARENT_3D]), std::end(_commands[QUEUE_GROUP::TRANSPARENT_3D]), compare3DCommand); std::sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_NEG]), std::end(_commands[QUEUE_GROUP::GLOBALZ_NEG]), compareRenderCommand); std::sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_POS]), std::end(_commands[QUEUE_GROUP::GLOBALZ_POS]), compareRenderCommand);}void Renderer::visitRenderQueue(RenderQueue& queue){ queue.saveRenderState(); // //Process Global-Z < 0 Objects // // //Process Opaque Object // // //Process 3D Transparent object // // //Process Global-Z = 0 Queue // const auto& zZeroQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_ZERO); if (zZeroQueue.size() > 0) { // false if(_isDepthTestFor2D) { glEnable(GL_DEPTH_TEST); glDepthMask(true); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(true); RenderState::StateBlock::_defaultState->setDepthWrite(true); RenderState::StateBlock::_defaultState->setBlend(true); } else { glDisable(GL_DEPTH_TEST); glDepthMask(false); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(false); RenderState::StateBlock::_defaultState->setDepthWrite(false); RenderState::StateBlock::_defaultState->setBlend(true); } glDisable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setCullFace(false); for (auto it = zZeroQueue.cbegin(); it != zZeroQueue.cend(); ++it) { processRenderCommand(*it); } flush(); } // //Process Global-Z > 0 Queue // queue.restoreRenderState();}void Renderer::processRenderCommand(RenderCommand* command){ auto commandType = command->getType(); if( RenderCommand::Type::TRIANGLES_COMMAND == commandType) { // flush other queues flush3D(); auto cmd = static_cast<TrianglesCommand*>(command); // flush own queue when buffer is full if(_filledVertex + cmd->getVertexCount() > VBO_SIZE || _filledIndex + cmd->getIndexCount() > INDEX_VBO_SIZE) { CCASSERT(cmd->getVertexCount()>= 0 && cmd->getVertexCount() < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command"); CCASSERT(cmd->getIndexCount()>= 0 && cmd->getIndexCount() < INDEX_VBO_SIZE, "VBO for index is not big enough, please break the data down or use customized render command"); drawBatchedTriangles(); } // queue it, draw when vector full or all cmd queued _queuedTriangleCommands.push_back(cmd); _filledIndex += cmd->getIndexCount(); _filledVertex += cmd->getVertexCount(); }}void Renderer::flush(){ flush2D(); flush3D();}void Renderer::flush2D(){ flushTriangles();}void Renderer::flushTriangles(){ drawBatchedTriangles();}void Renderer::fillVerticesAndIndices(const TrianglesCommand* cmd){ memcpy(&_verts[_filledVertex], cmd->getVertices(), sizeof(V3F_C4B_T2F) * cmd->getVertexCount()); // fill vertex, and convert them to world coordinates const Mat4& modelView = cmd->getModelView(); for(ssize_t i=0; i < cmd->getVertexCount(); ++i) { modelView.transformPoint(&(_verts[i + _filledVertex].vertices)); } // fill index const unsigned short* indices = cmd->getIndices(); for(ssize_t i=0; i< cmd->getIndexCount(); ++i) { _indices[_filledIndex + i] = _filledVertex + indices[i]; } _filledVertex += cmd->getVertexCount(); _filledIndex += cmd->getIndexCount();}void Renderer::drawBatchedTriangles(){ if(_queuedTriangleCommands.empty()) return; CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_TRIANGLES"); _filledVertex = 0; _filledIndex = 0; /************** 1: Setup up vertices/indices *************/ _triBatchesToDraw[0].offset = 0; _triBatchesToDraw[0].indicesToDraw = 0; _triBatchesToDraw[0].cmd = nullptr; int batchesTotal = 0; int prevMaterialID = -1; bool firstCommand = true; for(auto it = std::begin(_queuedTriangleCommands); it != std::end(_queuedTriangleCommands); ++it) { const auto& cmd = *it; auto currentMaterialID = cmd->getMaterialID(); const bool batchable = !cmd->isSkipBatching(); // fill vertices and indices from cmd, also convert vertices to world coordinates fillVerticesAndIndices(cmd); // in the same batch ? if (batchable && (prevMaterialID == currentMaterialID || firstCommand)) { CC_ASSERT(firstCommand || _triBatchesToDraw[batchesTotal].cmd->getMaterialID() == cmd->getMaterialID() && "argh... error in logic"); _triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount(); _triBatchesToDraw[batchesTotal].cmd = cmd; } else { // is this the first one? if (!firstCommand) { batchesTotal++; _triBatchesToDraw[batchesTotal].offset = _triBatchesToDraw[batchesTotal-1].offset + _triBatchesToDraw[batchesTotal-1].indicesToDraw; } _triBatchesToDraw[batchesTotal].cmd = cmd; _triBatchesToDraw[batchesTotal].indicesToDraw = (int) cmd->getIndexCount(); // is this a single batch ? Prevent creating a batch group then if (!batchable) currentMaterialID = -1; } // capacity full ? if (batchesTotal + 1 >= _triBatchesToDrawCapacity) { _triBatchesToDrawCapacity *= 1.4; _triBatchesToDraw = (TriBatchToDraw*) realloc(_triBatchesToDraw, sizeof(_triBatchesToDraw[0]) * _triBatchesToDrawCapacity); } prevMaterialID = currentMaterialID; firstCommand = false; } batchesTotal++; /************** 2: Copy vertices/indices to GL objects *************/ // false if (conf->supportsShareableVAO() && conf->supportsMapBuffer()) { //Bind VAO GL::bindVAO(_buffersVAO); //Set VBO data glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); // option 1: subdata// glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] ); // option 2: data// glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, _verts, GL_STATIC_DRAW); // option 3: orphaning + glMapBuffer // FIXME: in order to work as fast as possible, it must "and the exact same size and usage hints it had before." // source: https://www.opengl.org/wiki/Buffer_Object_Streaming#Explicit_multiple_buffering // so most probably we won't have any benefit of using it glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, nullptr, GL_STATIC_DRAW); void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); memcpy(buf, _verts, sizeof(_verts[0]) * _filledVertex); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); } else { // Client Side Arrays#define kQuadSize sizeof(_verts[0]) glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex , _verts, GL_DYNAMIC_DRAW); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); // vertices glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices)); // colors glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors)); // tex coords glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); } /************** 3: Draw *************/ for (int i=0; i<batchesTotal; ++i) { CC_ASSERT(_triBatchesToDraw[i].cmd && "Invalid batch"); _triBatchesToDraw[i].cmd->useMaterial(); glDrawElements(GL_TRIANGLES, (GLsizei) _triBatchesToDraw[i].indicesToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (_triBatchesToDraw[i].offset*sizeof(_indices[0])) ); _drawnBatches++; _drawnVertices += _triBatchesToDraw[i].indicesToDraw; } /************** 4: Cleanup *************/ if (Configuration::getInstance()->supportsShareableVAO()) { //Unbind VAO GL::bindVAO(0); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } _queuedTriangleCommands.clear(); _filledVertex = 0; _filledIndex = 0;}void TrianglesCommand::useMaterial() const{ //Set texture GL::bindTexture2D(_textureID); if (_alphaTextureID > 0) { // ANDROID ETC1 ALPHA supports. GL::bindTexture2DN(1, _alphaTextureID); } //set blend mode GL::blendFunc(_blendType.src, _blendType.dst); _glProgramState->apply(_mv);}void GLProgramState::apply(const Mat4& modelView){ applyGLProgram(modelView); applyAttributes(); applyUniforms();}void GLProgramState::applyGLProgram(const Mat4& modelView){ CCASSERT(_glprogram, "invalid glprogram"); updateUniformsAndAttributes(); // set shader _glprogram->use(); _glprogram->setUniformsForBuiltins(modelView);}

缺陷: 1)每次render都会重新更新VBO中的vertex data,可能这样比较适合 2)glVertexAttribPointer和GLProgramState分离,GLProgramState并不起作用


上一篇:maven项目的创建

下一篇:layout_weight 讲解

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表