CollisionFeature::CollisionFeature(const std::vector<Coordinate> &line, const Anchor &anchor, const float top, const float bottom, const float left, const float right, const float boxScale, const float padding, const bool alongLine, const bool straight) { if (top == 0 && bottom == 0 && left == 0 && right == 0) return; const float y1 = top * boxScale - padding; const float y2 = bottom * boxScale + padding; const float x1 = left * boxScale - padding; const float x2 = right * boxScale + padding; if (alongLine) { float height = y2 - y1; const float length = x2 - x1; if (height <= 0.0f) return; height = std::max(10.0f * boxScale, height); Coordinate anchorPoint(int16_t(anchor.x), int16_t(anchor.y)); if (straight) { // used for icon labels that are aligned with the line, but don't curve along it const vec2<double> vector = util::unit(vec2<double>(line[anchor.segment + 1] - line[anchor.segment])) * length; const std::vector<Coordinate> newLine({ anchorPoint - vector, anchorPoint + vector }); bboxifyLabel(newLine, anchorPoint, 0, length, height); } else { // used for text labels that curve along a line bboxifyLabel(line, anchorPoint, anchor.segment, length, height); } } else { boxes.emplace_back(anchor, x1, y1, x2, y2, std::numeric_limits<float>::infinity()); } }
/* \reimp (GraphicsLayer.h) */ void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value) { if (value == anchorPoint()) return; GraphicsLayer::setAnchorPoint(value); notifyChange(TextureMapperLayer::AnchorPointChange); }
void WebGraphicsLayer::setAnchorPoint(const FloatPoint3D& p) { if (anchorPoint() == p) return; GraphicsLayer::setAnchorPoint(p); notifyChange(); }
void WebGraphicsLayer::setAnchorPoint(const FloatPoint3D& p) { if (anchorPoint() == p) return; GraphicsLayer::setAnchorPoint(p); didChangeGeometry(); }
void CoordinatedGraphicsLayer::setAnchorPoint(const FloatPoint3D& p) { if (anchorPoint() == p) return; GraphicsLayer::setAnchorPoint(p); m_layerState.anchorPointChanged = true; didChangeGeometry(); }
void WebGraphicsLayer::computeTransformedVisibleRect() { if (!m_shouldUpdateVisibleRect) return; m_shouldUpdateVisibleRect = false; m_layerTransform.setLocalTransform(transform()); m_layerTransform.setPosition(position()); m_layerTransform.setAnchorPoint(anchorPoint()); m_layerTransform.setSize(size()); m_layerTransform.setFlattening(!preserves3D()); m_layerTransform.setChildrenTransform(childrenTransform()); m_layerTransform.combineTransforms(parent() ? toWebGraphicsLayer(parent())->m_layerTransform.combinedForChildren() : TransformationMatrix()); // The combined transform will be used in tiledBackingStoreVisibleRect. adjustVisibleRect(); adjustContentsScale(); }
void WebGraphicsLayer::syncCompositingStateForThisLayerOnly() { if (!m_layerTreeTileClient) m_layerTreeTileClient = layerTreeTileClient(); updateContentBuffers(); if (!m_modified) return; m_layerInfo.name = name(); m_layerInfo.anchorPoint = anchorPoint(); m_layerInfo.backfaceVisible = backfaceVisibility(); m_layerInfo.childrenTransform = childrenTransform(); m_layerInfo.contentsOpaque = contentsOpaque(); m_layerInfo.contentsRect = contentsRect(); m_layerInfo.drawsContent = drawsContent(); m_layerInfo.mask = toWebLayerID(maskLayer()); m_layerInfo.masksToBounds = masksToBounds(); m_layerInfo.opacity = opacity(); m_layerInfo.parent = toWebLayerID(parent()); m_layerInfo.pos = position(); m_layerInfo.preserves3D = preserves3D(); m_layerInfo.replica = toWebLayerID(replicaLayer()); m_layerInfo.size = size(); m_layerInfo.transform = transform(); m_contentNeedsDisplay = false; m_layerInfo.children.clear(); for (size_t i = 0; i < children().size(); ++i) m_layerInfo.children.append(toWebLayerID(children()[i])); ASSERT(m_layerTreeTileClient); if (m_layerInfo.imageIsUpdated && m_image && !m_layerInfo.imageBackingStoreID) m_layerInfo.imageBackingStoreID = m_layerTreeTileClient->adoptImageBackingStore(m_image.get()); m_layerTreeTileClient->didSyncCompositingStateForLayer(m_layerInfo); m_modified = false; m_layerInfo.imageIsUpdated = false; if (m_hasPendingAnimations) notifyAnimationStarted(WTF::currentTime()); m_layerInfo.animations.clear(); m_hasPendingAnimations = false; }
void WebGraphicsLayer::syncLayerState() { if (!m_shouldSyncLayerState) return; m_shouldSyncLayerState = false; m_layerInfo.anchorPoint = anchorPoint(); m_layerInfo.backfaceVisible = backfaceVisibility(); m_layerInfo.childrenTransform = childrenTransform(); m_layerInfo.contentsOpaque = contentsOpaque(); m_layerInfo.contentsRect = contentsRect(); m_layerInfo.drawsContent = drawsContent(); m_layerInfo.mask = toWebLayerID(maskLayer()); m_layerInfo.masksToBounds = masksToBounds(); m_layerInfo.opacity = opacity(); m_layerInfo.parent = toWebLayerID(parent()); m_layerInfo.pos = position(); m_layerInfo.preserves3D = preserves3D(); m_layerInfo.replica = toWebLayerID(replicaLayer()); m_layerInfo.size = size(); m_layerInfo.transform = transform(); m_webGraphicsLayerClient->syncLayerState(m_id, m_layerInfo); }
void GraphicsLayerTextureMapper::commitLayerChanges() { if (m_changeMask == NoChanges) return; if (m_changeMask & ChildrenChange) { Vector<TextureMapperLayer*> textureMapperLayerChildren; toTextureMapperLayerVector(children(), textureMapperLayerChildren); m_layer->setChildren(textureMapperLayerChildren); } if (m_changeMask & MaskLayerChange) m_layer->setMaskLayer(toTextureMapperLayer(maskLayer())); if (m_changeMask & ReplicaLayerChange) m_layer->setReplicaLayer(toTextureMapperLayer(replicaLayer())); if (m_changeMask & PositionChange) m_layer->setPosition(position()); if (m_changeMask & AnchorPointChange) m_layer->setAnchorPoint(anchorPoint()); if (m_changeMask & SizeChange) m_layer->setSize(size()); if (m_changeMask & TransformChange) m_layer->setTransform(transform()); if (m_changeMask & ChildrenTransformChange) m_layer->setChildrenTransform(childrenTransform()); if (m_changeMask & Preserves3DChange) m_layer->setPreserves3D(preserves3D()); if (m_changeMask & ContentsRectChange) m_layer->setContentsRect(contentsRect()); if (m_changeMask & MasksToBoundsChange) m_layer->setMasksToBounds(masksToBounds()); if (m_changeMask & DrawsContentChange) m_layer->setDrawsContent(drawsContent()); if (m_changeMask & ContentsVisibleChange) m_layer->setContentsVisible(contentsAreVisible()); if (m_changeMask & ContentsOpaqueChange) m_layer->setContentsOpaque(contentsOpaque()); if (m_changeMask & BackfaceVisibilityChange) m_layer->setBackfaceVisibility(backfaceVisibility()); if (m_changeMask & OpacityChange) m_layer->setOpacity(opacity()); if (m_changeMask & BackgroundColorChange) m_layer->setSolidColor(solidColor()); #if ENABLE(CSS_FILTERS) if (m_changeMask & FilterChange) m_layer->setFilters(filters()); #endif if (m_changeMask & BackingStoreChange) m_layer->setBackingStore(m_backingStore); if (m_changeMask & DebugVisualsChange) m_layer->setDebugVisuals(isShowingDebugBorder(), debugBorderColor(), debugBorderWidth(), isShowingRepaintCounter()); if (m_changeMask & RepaintCountChange) m_layer->setRepaintCount(repaintCount()); if (m_changeMask & ContentChange) m_layer->setContentsLayer(platformLayer()); if (m_changeMask & AnimationChange) m_layer->setAnimations(m_animations); if (m_changeMask & AnimationStarted) client()->notifyAnimationStarted(this, m_animationStartTime); if (m_changeMask & FixedToViewporChange) m_layer->setFixedToViewport(fixedToViewport()); if (m_changeMask & IsScrollableChange) m_layer->setIsScrollable(isScrollable()); if (m_changeMask & CommittedScrollOffsetChange) m_layer->didCommitScrollOffset(m_committedScrollOffset); m_changeMask = NoChanges; }
/* * Load a given world, i.e. read the world from the `levels' data structure and * convert it into a Box2D world. */ void load_world(unsigned int level) { // Pause the game when loading a new level play = 0; if (level >= num_levels) { // Note that level is unsigned but we still use %d so -1 is shown as // such. printf("Warning: level %d does not exist.\n", level); return; } // Create a Box2D world and populate it with all bodies for this level // (including the ball). current_level = level + 1; cur_level = &(levels[level]); init(cur_level->num_joints); b2Vec2 gravity (0, -9.81); bool do_sleep = true; world = new b2World(gravity); world->SetAllowSleeping(do_sleep); // printf("num_polygons = %i\n", levels[0].num_polygons); // printf("is_Dynamic = %d\n", levels[0].polygons[0].is_dynamic); // Create ground b2BodyDef groundBodyDef; groundBodyDef.position.Set(0.0f, -world_y / 2); ground = world->CreateBody(&groundBodyDef); // addBodyToCreatedBodies(ground, num_created_bodies++); b2PolygonShape groundBox; groundBox.SetAsBox(world_x, world_y / 2); ground->CreateFixture(&groundBox, 0.0f); // Setup rest of level. for (unsigned int i = 0; i < cur_level->num_polygons; i++) { b2Vec2 *vertices = new b2Vec2[cur_level->polygons[i].num_verts]; for (unsigned int j = 0; j < cur_level->polygons[i].num_verts; j ++) { vertices[j].Set(cur_level->polygons[i].verts[j].x, cur_level->polygons[i].verts[j].y); } makePolygon(cur_level->polygons[i].is_dynamic, cur_level->polygons[i].position.x, cur_level->polygons[i].position.y, vertices, cur_level->polygons[i].num_verts, num_created_bodies++); delete[] vertices; } // Create ball b2BodyDef ballBodyDef; ballBodyDef.type = b2_dynamicBody; ballBodyDef.position.Set(cur_level->start.x, cur_level->start.y); ball = world->CreateBody(&ballBodyDef); b2CircleShape dynamicCircle; dynamicCircle.m_radius = ball_radius; b2FixtureDef fixtureDef; fixtureDef.shape = &dynamicCircle; fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f; ball->CreateFixture(&fixtureDef); // Setup joints. for (unsigned int i = 0; i < cur_level->num_joints; i++) { if (cur_level->joints[i].joint_type == JOINT_REVOLUTE) { b2RevoluteJointDef jointDef; // jointDef.anchorPoint = objectA->GetPosition(); b2Body *objectA = created_bodies[cur_level->joints[i].objectA]; b2Body *objectB = created_bodies[cur_level->joints[i].objectB]; b2Vec2 anchorPoint(cur_level->joints[i].anchor.x, cur_level->joints[i].anchor.y); jointDef.Initialize(objectA, objectB, anchorPoint); world->CreateJoint(&jointDef); } else if (cur_level->joints[i].joint_type == JOINT_PULLEY) { b2Vec2 anchorPoint1(cur_level->joints[i].anchor.x, cur_level->joints[i].anchor.x); b2Vec2 anchorPoint2(cur_level->joints[i].pulley.anchor2.x, cur_level->joints[i].pulley.anchor2.x); b2Vec2 groundAnchor1(cur_level->joints[i].pulley.ground1.x, cur_level->joints[i].pulley.ground1.y); b2Vec2 groundAnchor2(cur_level->joints[i].pulley.ground2.x, cur_level->joints[i].pulley.ground2.y); float ratio = cur_level->joints[i].pulley.ratio; b2Body *objectA = created_bodies[cur_level->joints[i].objectA]; b2Body *objectB = created_bodies[cur_level->joints[i].objectB]; b2PulleyJointDef jointDef; jointDef.Initialize(objectA, objectB, groundAnchor1, groundAnchor2, anchorPoint1, anchorPoint2, ratio); world->CreateJoint(&jointDef); } } }
bool BillBoard::calculateBillbaordTransform() { //Get camera world position auto camera = Camera::getVisitingCamera(); const Mat4& camWorldMat = camera->getNodeToWorldTransform(); //TODO: use math lib to calculate math lib Make it easier to read and maintain if (memcmp(_camWorldMat.m, camWorldMat.m, sizeof(float) * 16) != 0 || memcmp(_mvTransform.m, _modelViewTransform.m, sizeof(float) * 16) != 0 || _modeDirty || true) { //Rotate based on anchor point Vec3 anchorPoint(_anchorPointInPoints.x , _anchorPointInPoints.y , 0.0f); Mat4 localToWorld = _modelViewTransform; localToWorld.translate(anchorPoint); //Decide billboard mode Vec3 camDir; switch (_mode) { case Mode::VIEW_POINT_ORIENTED: camDir.set(localToWorld.m[12] - camWorldMat.m[12], localToWorld.m[13] - camWorldMat.m[13], localToWorld.m[14] - camWorldMat.m[14]); break; case Mode::VIEW_PLANE_ORIENTED: camWorldMat.transformVector(Vec3(0.0f, 0.0f, -1.0f), &camDir); break; default: CCASSERT(false, "invalid billboard mode"); break; } _modeDirty = false; if (camDir.length() < MATH_TOLERANCE) { camDir.set(camWorldMat.m[8], camWorldMat.m[9], camWorldMat.m[10]); } camDir.normalize(); Quaternion rotationQuaternion; this->getNodeToWorldTransform().getRotation(&rotationQuaternion); Mat4 rotationMatrix; rotationMatrix.setIdentity(); Vec3 upAxis(rotationMatrix.m[4],rotationMatrix.m[5],rotationMatrix.m[6]); Vec3 x, y; camWorldMat.transformVector(upAxis, &y); Vec3::cross(camDir, y, &x); x.normalize(); Vec3::cross(x, camDir, &y); y.normalize(); float xlen = sqrtf(localToWorld.m[0] * localToWorld.m[0] + localToWorld.m[1] * localToWorld.m[1] + localToWorld.m[2] * localToWorld.m[2]); float ylen = sqrtf(localToWorld.m[4] * localToWorld.m[4] + localToWorld.m[5] * localToWorld.m[5] + localToWorld.m[6] * localToWorld.m[6]); float zlen = sqrtf(localToWorld.m[8] * localToWorld.m[8] + localToWorld.m[9] * localToWorld.m[9] + localToWorld.m[10] * localToWorld.m[10]); Mat4 billboardTransform; billboardTransform.m[0] = x.x * xlen; billboardTransform.m[1] = x.y * xlen; billboardTransform.m[2] = x.z * xlen; billboardTransform.m[4] = y.x * ylen; billboardTransform.m[5] = y.y * ylen; billboardTransform.m[6] = y.z * ylen; billboardTransform.m[8] = -camDir.x * zlen; billboardTransform.m[9] = -camDir.y * zlen; billboardTransform.m[10] = -camDir.z * zlen; billboardTransform.m[12] = localToWorld.m[12]; billboardTransform.m[13] = localToWorld.m[13]; billboardTransform.m[14] = localToWorld.m[14]; billboardTransform.translate(-anchorPoint); _mvTransform = _modelViewTransform = billboardTransform; _camWorldMat = camWorldMat; return true; } return false; }
void DrawGlyphs::apply(GraphicsContext& context) const { FontCascade::drawGlyphs(context, m_font, generateGlyphBuffer(), 0, m_glyphs.size(), anchorPoint(), m_smoothingMode); }
void WKCACFLayer::printLayer(int indent) const { CGPoint layerPosition = position(); CGPoint layerAnchorPoint = anchorPoint(); CGRect layerBounds = bounds(); printIndent(indent); fprintf(stderr, "(%s [%g %g %g] [%g %g %g %g] [%g %g %g]\n", isTransformLayer() ? "transform-layer" : "layer", layerPosition.x, layerPosition.y, zPosition(), layerBounds.origin.x, layerBounds.origin.y, layerBounds.size.width, layerBounds.size.height, layerAnchorPoint.x, layerAnchorPoint.y, anchorPointZ()); // Print name if needed String layerName = name(); if (!layerName.isEmpty()) { printIndent(indent + 1); fprintf(stderr, "(name %s)\n", layerName.utf8().data()); } // Print masksToBounds if needed bool layerMasksToBounds = masksToBounds(); if (layerMasksToBounds) { printIndent(indent + 1); fprintf(stderr, "(masksToBounds true)\n"); } // Print opacity if needed float layerOpacity = opacity(); if (layerOpacity != 1) { printIndent(indent + 1); fprintf(stderr, "(opacity %hf)\n", layerOpacity); } // Print sublayerTransform if needed CATransform3D layerTransform = sublayerTransform(); if (!CATransform3DIsIdentity(layerTransform)) { printIndent(indent + 1); fprintf(stderr, "(sublayerTransform "); printTransform(layerTransform); fprintf(stderr, ")\n"); } // Print transform if needed layerTransform = transform(); if (!CATransform3DIsIdentity(layerTransform)) { printIndent(indent + 1); fprintf(stderr, "(transform "); printTransform(layerTransform); fprintf(stderr, ")\n"); } // Print contents if needed CGImageRef layerContents = contents(); if (layerContents) { printIndent(indent + 1); fprintf(stderr, "(contents (image [%d %d]))\n", CGImageGetWidth(layerContents), CGImageGetHeight(layerContents)); } // Print sublayers if needed int n = numSublayers(); if (n > 0) { printIndent(indent + 1); fprintf(stderr, "(sublayers\n"); for (int i = 0; i < n; ++i) sublayerAtIndex(i)->printLayer(indent + 2); printIndent(indent + 1); fprintf(stderr, ")\n"); } printIndent(indent); fprintf(stderr, ")\n"); }
void BillBoard::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) { auto camera = Camera::getVisitingCamera(); const Mat4& camWorldMat = camera->getNodeToWorldTransform(); if (memcmp(_camWorldMat.m, camWorldMat.m, sizeof(float) * 16) != 0 || memcmp(_mvTransform.m, transform.m, sizeof(float) * 16) != 0 || _modeDirty) { Vec3 anchorPoint(_anchorPointInPoints.x , _anchorPointInPoints.y , 0.0f); Mat4 localToWorld = transform; localToWorld.translate(anchorPoint); Vec3 camDir; switch (_mode) { case Mode::VIEW_POINT_ORIENTED: camDir = Vec3(localToWorld.m[12] - camWorldMat.m[12], localToWorld.m[13] - camWorldMat.m[13], localToWorld.m[14] - camWorldMat.m[14]); break; case Mode::VIEW_PLANE_ORIENTED: camWorldMat.transformVector(Vec3(0.0f, 0.0f, -1.0f), &camDir); break; default: CCASSERT(false, "invalid billboard mode"); break; } _modeDirty = false; if (camDir.length() < MATH_TOLERANCE) { camDir.set(camWorldMat.m[8], camWorldMat.m[9], camWorldMat.m[10]); } camDir.normalize(); Quaternion rotationQuaternion; this->getNodeToWorldTransform().getRotation(&rotationQuaternion); // fetch the rotation angle of z float rotationZ = atan2(2*(rotationQuaternion.w*rotationQuaternion.z + rotationQuaternion.x*rotationQuaternion.y), (1 - 2* (rotationQuaternion.y*rotationQuaternion.y + rotationQuaternion.z *rotationQuaternion.z))); Mat4 rotationMatrix; rotationMatrix.setIdentity(); rotationMatrix.rotateZ(rotationZ); Vec3 upAxis = Vec3(rotationMatrix.m[4],rotationMatrix.m[5],rotationMatrix.m[6]); Vec3 x, y; camWorldMat.transformVector(upAxis, &y); Vec3::cross(camDir, y, &x); x.normalize(); Vec3::cross(x, camDir, &y); y.normalize(); float xlen = sqrtf(localToWorld.m[0] * localToWorld.m[0] + localToWorld.m[1] * localToWorld.m[1] + localToWorld.m[2] * localToWorld.m[2]); float ylen = sqrtf(localToWorld.m[4] * localToWorld.m[4] + localToWorld.m[5] * localToWorld.m[5] + localToWorld.m[6] * localToWorld.m[6]); float zlen = sqrtf(localToWorld.m[8] * localToWorld.m[8] + localToWorld.m[9] * localToWorld.m[9] + localToWorld.m[10] * localToWorld.m[10]); _billboardTransform.m[0] = x.x * xlen; _billboardTransform.m[1] = x.y * xlen; _billboardTransform.m[2] = x.z * xlen; _billboardTransform.m[4] = y.x * ylen; _billboardTransform.m[5] = y.y * ylen; _billboardTransform.m[6] = y.z * ylen; _billboardTransform.m[8] = -camDir.x * zlen; _billboardTransform.m[9] = -camDir.y * zlen; _billboardTransform.m[10] = -camDir.z * zlen; _billboardTransform.m[12] = localToWorld.m[12]; _billboardTransform.m[13] = localToWorld.m[13]; _billboardTransform.m[14] = localToWorld.m[14]; _billboardTransform.translate(-anchorPoint); const Mat4 &viewMat = camWorldMat.getInversed(); _zDepthInView = -(viewMat.m[2] * _billboardTransform.m[12] + viewMat.m[6] * _billboardTransform.m[13] + viewMat.m[10] * _billboardTransform.m[14] + viewMat.m[14]); _mvTransform = transform; _camWorldMat = camWorldMat; } //FIXME: frustum culling here { _quadCommand.init(_zDepthInView, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, _billboardTransform); _quadCommand.setTransparent(true); renderer->addCommand(&_quadCommand); } }
const Mat4& Node::getNodeToParentTransform() const { if (_transformDirty) { // Translate values float x = _position.x; float y = _position.y; float z = _positionZ; if (_ignoreAnchorPointForPosition) { x += _anchorPointInPoints.x; y += _anchorPointInPoints.y; } bool needsSkewMatrix = ( _skewX || _skewY ); Vec2 anchorPoint(_anchorPointInPoints.x * _scaleX, _anchorPointInPoints.y * _scaleY); // calculate real position 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 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 _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; } } if (_useAdditionalTransform) { _transform = _transform * _additionalTransform; } _transformDirty = false; } return _transform; }