I'm wondering if anyone has a trick to keep the mouse position centered in a (QGL)widget for Qt. I read that one could set the mouseposition after finding the delta, but this way works very buggy for me. Mouse events are not properly registered, any if they do, very jumpy.
void World::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
player->rotHorizontal += 360.0 * dx;
if(player->rotHorizontal < 0.0)
player->rotHorizontal += 360.0;
else if(player->rotHorizontal > +360.0)
player->rotHorizontal -= 360.0;
player->rotVertical += 360.0 * dy;
if (player->rotVertical > MAX_ROTATION_UP) {
player->rotVertical = MAX_ROTATION_UP;
} else if (player->rotVertical < -MAX_ROTATION_UP) {
player->rotVertical = -MAX_ROTATION_UP;
}
}
// int diffX = event->pos().x() - lastPos.x() % 20;
// int diffY = event->pos().y() - lastPos.y() % 20;
// if (diffY > 10 || diffX > 10 || diffY < -10 || diffX < -10) {
// QPoint glob = mapToGlobal(QPoint(this->pos().x() + width()/2, this->pos().y() + height()/2));
// QCursor::setPos(glob);
// }
lastPos = event->pos();
QGLWidget::mouseMoveEvent(event);
}
I commented out the code which would keep the mouse centered. If this would work, I would place it in the leftclick area.
Fixed:
void World::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
player->rotHorizontal += 360.0 * dx;
if(player->rotHorizontal < 0.0)
player->rotHorizontal += 360.0;
else if(player->rotHorizontal > +360.0)
player->rotHorizontal -= 360.0;
player->rotVertical += 360.0 * dy;
if (player->rotVertical > MAX_ROTATION_UP) {
player->rotVertical = MAX_ROTATION_UP;
} else if (player->rotVertical < -MAX_ROTATION_UP) {
player->rotVertical = -MAX_ROTATION_UP;
}
}
QPoint glob = mapToGlobal(QPoint(width()/2,height()/2));
QCursor::setPos(glob);
lastPos = QPoint(width()/2,height()/2);
QGLWidget::mouseMoveEvent(event);
}
Related
I'm currently working on a pathtracer in c and open cl.
I'm using this algorithm for rendering. The first collision works well, however, from the second collision onwards there is a dark shadow on the lower side of the voxels.
This is the color of the voxel the initial ray hits:
result
This is the color of the voxel that the second ray hits:
result
And this is the result after rendering to a depth of 1000:
result
This is the code I used (openCL):
int cast_ray(Renderer *r, Ray ray, float3 *hitPos, int3 *normal, Material *material) {
int3 voxel = convert_int3(ray.origin);
int3 step = {
(ray.direction.x >= 0) ? 1 : -1,
(ray.direction.y >= 0) ? 1 : -1,
(ray.direction.z >= 0) ? 1 : -1
};
float3 tMax = {
(ray.direction.x != 0) ? (voxel.x + step.x - ray.origin.x) / ray.direction.x : MAXFLOAT,
(ray.direction.y != 0) ? (voxel.y + step.y - ray.origin.y) / ray.direction.y : MAXFLOAT,
(ray.direction.z != 0) ? (voxel.z + step.z - ray.origin.z) / ray.direction.z : MAXFLOAT
};
float3 tDelta = {
(ray.direction.x != 0) ? 1 / ray.direction.x * step.x : MAXFLOAT,
(ray.direction.y != 0) ? 1 / ray.direction.y * step.y : MAXFLOAT,
(ray.direction.z != 0) ? 1 / ray.direction.z * step.z : MAXFLOAT
};
int side;
while(1) {
if(tMax.x < tMax.y) {
if(tMax.x < tMax.z) {
voxel.x += step.x;
tMax.x += tDelta.x;
side = 0;
} else {
voxel.z += step.z;
tMax.z += tDelta.z;
side = 2;
}
} else {
if(tMax.y < tMax.z) {
voxel.y += step.y;
tMax.y += tDelta.y;
side = 1;
} else {
voxel.z += step.z;
tMax.z += tDelta.z;
side = 2;
}
}
if(out_of_scene(r, voxel))
return 0;
MaterialID id = get_material_ID(r, voxel);
if(id == 0)
continue;
*material = get_material(r, id);
switch(side) {
case 0:
hitPos->x = (float)voxel.x;
hitPos->y = ray.origin.y + (hitPos->x - ray.origin.x) * ray.direction.y / ray.direction.x;
hitPos->z = ray.origin.z + (hitPos->x - ray.origin.x) * ray.direction.z / ray.direction.x;
*normal = (int3){-step.x, 0, 0};
break;
case 1:
hitPos->y = (float)voxel.y;
hitPos->x = ray.origin.x + (hitPos->y - ray.origin.y) * ray.direction.x / ray.direction.y;
hitPos->z = ray.origin.z + (hitPos->y - ray.origin.y) * ray.direction.z / ray.direction.y;
*normal = (int3){0, -step.y, 0};
break;
case 2:
hitPos->z = (float)voxel.z;
hitPos->y = ray.origin.y + (hitPos->z - ray.origin.z) * ray.direction.y / ray.direction.z;
hitPos->x = ray.origin.x + (hitPos->z - ray.origin.z) * ray.direction.x / ray.direction.z;
*normal = (int3){0, 0, -step.z};
break;
}
return 1;
}
}
float3 get_color(Renderer *r, Ray ray) {
float3 mask = 1;
float3 color = 0;
int maxDepth = 1000;
for(int i = 0; i < maxDepth; i++) {
float3 hitPos;
int3 iNormal;
Material material;
if(cast_ray(r, ray, &hitPos, &iNormal, &material)) {
float3 fNormal = convert_float3(iNormal);
if(material.type == 1) {
color = mask * material.color;
break;
} else if(material.type == 2) {
float3 direction = fNormal + random_unit_vector(r->rng);
ray = (Ray){hitPos, direction};
mask *= material.color;
} else if(material.type == 3) {
float3 direction = reflection_dir(ray.direction, fNormal) + random_unit_vector(r->rng) * material.fuzzyness;
ray = (Ray){hitPos, direction};
mask = mask * (1 - material.tint) + mask * material.color * material.tint;
}
} else {
color = mask * r->bgColor;
break;
}
// if(i == 1)
// return material.color;
}
return color;
}
I think that the problem is that the new origin of the ray is somehow not correct, but I can't find a way to fix it.
I have written a small program that renders a 175x175 heightmap. The rendering is done using Qt3D which is basically a set of wrappers around OpenGL. The program loads fine and runs fine on a powerful desktop. However, when I run it on a lower power GPU, image updates are very choppy when I start moving the camera around. Rendering 3D terrain mesh really shouldn't be that difficult for even a small GPU, so I assume I am doing something very wrong. Are there some obvious ways to optimize this code or am I just expecting too much from a small GPU?
Fragment shader
https://github.com/qt/qt3d/blob/5.12/src/extras/shaders/es2/phong.inc.frag
Vertex shader:
https://github.com/qt/qt3d/blob/5.12/src/extras/shaders/es2/morphphong.vert
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow view;
// Scene Root
Qt3DCore::QEntity *sceneRoot = new Qt3DCore::QEntity();
// Scene Camera
Qt3DRender::QCamera *basicCamera = view.camera();
basicCamera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection);
basicCamera->setUpVector(QVector3D(0.0f, 1.0f, 0.0f));
basicCamera->setViewCenter(QVector3D(60.0f, 15.0f, -60.0f));
basicCamera->setPosition(QVector3D(60.0f, 26.0f, 0.0f));
// For camera controls
Qt3DExtras::QFirstPersonCameraController *camController = new Qt3DExtras::QFirstPersonCameraController(sceneRoot);
camController->setCamera(basicCamera);
// Material
Qt3DRender::QMaterial *material= new Qt3DExtras::QPhongMaterial(sceneRoot);
Qt3DCore::QEntity *customMeshEntity = new Qt3DCore::QEntity(sceneRoot);
// Transform
Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;
Qt3DRender::QGeometryRenderer *customMeshRenderer = new Qt3DRender::QGeometryRenderer;
Qt3DRender::QGeometry *customGeometry = new Qt3DRender::QGeometry(customMeshRenderer);
Qt3DRender::QBuffer *vertexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, customGeometry);
Qt3DRender::QBuffer *indexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, customGeometry);
QImage heightmap("../assets/heightmap.png");
QByteArray vertexBufferData;
vertexBufferData.resize(heightmap.width() * heightmap.height() * (3 + 3 + 3) * sizeof(float));
QVector<QVector3D> vertexPositions;
for (int row = 0; row < heightmap.height(); row++) {
for (int column = 0; column < heightmap.width(); column++) {
vertexPositions.append(QVector3D(row, heightmap.pixelColor(row, column).red()/8.0, -column));
}
}
QVector<QVector3D> vertexNormals;
for (int row = 0; row < heightmap.height(); row++) {
for (int column = 0; column < heightmap.width(); column++) {
int center = (row * heightmap.width()) + column;
int upper = center - heightmap.width();
int lower = center + heightmap.width();
int right = center + 1;
int left = center -1;
int lowerLeft = center - 1 + heightmap.width();
int upperRight = center + 1 - heightmap.width();
int rightEdge = heightmap.width() - 1;
int bottomEdge = heightmap.height() -1;
// Calculate normals for each adjacent face and sum
// Check for edge conditions
QVector3D vertexNormal(0, 0, 0);
if (column != 0 && row != 0 ) {
vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[upper], vertexPositions[left]);
}
if (column != rightEdge && row != 0) {
vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[upperRight], vertexPositions[upper]);
vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[right], vertexPositions[upperRight]);
}
if (column != rightEdge && row != bottomEdge) {
vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[lower], vertexPositions[right]);
}
if (column != 0 && row != bottomEdge) {
vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[lowerLeft], vertexPositions[lower]);
vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[left], vertexPositions[lowerLeft]);
}
vertexNormals.append(vertexNormal.normalized());
}
}
// Colors
QVector3D red(1.0f, 0.0f, 0.0f);
QVector3D yellow(1.0f, 1.0f, 0.0f);
QVector3D green(0.0f, 1.0f, 0.0f);
QVector3D blue(0.0f, 0.0f, 1.0f);
QVector3D white(1.0f, 1.0f, 1.0f);
QVector<QVector3D> vertices;
for (int i = 0; i < vertexPositions.count(); i ++) {
vertices.append(vertexPositions[i]);
vertices.append(vertexNormals[i]);
if (vertexPositions[i].y() > 20.0) {
vertices.append(red);
}
else if (vertexPositions[i].y() > 18.0) {
vertices.append(yellow);
}
else {
vertices.append(green);
}
}
float *rawVertexArray = reinterpret_cast<float *>(vertexBufferData.data());
int idx = 0;
Q_FOREACH (const QVector3D &v, vertices) {
rawVertexArray[idx++] = v.x();
rawVertexArray[idx++] = v.y();
rawVertexArray[idx++] = v.z();
}
// Indices
QByteArray indexBufferData;
int indicesCount = (heightmap.height() - 1) * (heightmap.width() - 1) * 2 * 3;
indexBufferData.resize( indicesCount * sizeof(uint));
uint *rawIndexArray = reinterpret_cast<uint *>(indexBufferData.data());
int index = 0;
for (int row = 0; row < heightmap.height()-1; row++) {
for (int column = 0; column < heightmap.width()-1; column++) {
// 1 <- 3
// | /
// | /
// v /
// 2
int vertexBufferIndex = (row * heightmap.width()) + column;
rawIndexArray[index++] = vertexBufferIndex;
rawIndexArray[index++] = vertexBufferIndex + heightmap.width(); // down one row
rawIndexArray[index++] = vertexBufferIndex + 1; // right one column
// 1
// / ^
// / |
// / |
// 2 -> 3
rawIndexArray[index++] = vertexBufferIndex + 1; // right one column
rawIndexArray[index++] = vertexBufferIndex + heightmap.width(); // down one row
rawIndexArray[index++] = vertexBufferIndex + heightmap.width() + 1; // down one row and right one column
}
}
vertexDataBuffer->setData(vertexBufferData);
indexDataBuffer->setData(indexBufferData);
// Attributes
Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute();
positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute);
positionAttribute->setBuffer(vertexDataBuffer);
positionAttribute->setDataType( Qt3DRender::QAttribute::Float);
positionAttribute->setDataSize(3);
positionAttribute->setByteOffset(0);
positionAttribute->setByteStride(9 * sizeof(float));
positionAttribute->setCount(vertexPositions.count());
positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName());
Qt3DRender::QAttribute *normalAttribute = new Qt3DRender::QAttribute();
normalAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute);
normalAttribute->setBuffer(vertexDataBuffer);
normalAttribute->setDataType( Qt3DRender::QAttribute::Float);
normalAttribute->setDataSize(3);
normalAttribute->setByteOffset(3 * sizeof(float));
normalAttribute->setByteStride(9 * sizeof(float));
normalAttribute->setCount(vertexPositions.count());
normalAttribute->setName( Qt3DRender::QAttribute::defaultNormalAttributeName());
Qt3DRender::QAttribute *colorAttribute = new Qt3DRender::QAttribute();
colorAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute);
colorAttribute->setBuffer(vertexDataBuffer);
colorAttribute->setDataType( Qt3DRender::QAttribute::Float);
colorAttribute->setDataSize(3);
colorAttribute->setByteOffset(6 * sizeof(float));
colorAttribute->setByteStride(9 * sizeof(float));
colorAttribute->setCount(vertexPositions.count());
colorAttribute->setName( Qt3DRender::QAttribute::defaultColorAttributeName());
Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute();
indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute);
indexAttribute->setBuffer(indexDataBuffer);
indexAttribute->setDataType( Qt3DRender::QAttribute::UnsignedInt);
indexAttribute->setDataSize(1);
indexAttribute->setByteOffset(0);
indexAttribute->setByteStride(0);
indexAttribute->setCount(indicesCount);
customGeometry->addAttribute(positionAttribute);
customGeometry->addAttribute(normalAttribute);
customGeometry->addAttribute(colorAttribute);
customGeometry->addAttribute(indexAttribute);
customMeshRenderer->setInstanceCount(1);
customMeshRenderer->setFirstVertex(0);
customMeshRenderer->setFirstInstance(0);
customMeshRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
customMeshRenderer->setGeometry(customGeometry);
customMeshEntity->addComponent(customMeshRenderer);
customMeshEntity->addComponent(transform);
customMeshEntity->addComponent(material);
view.setRootEntity(sceneRoot);
view.show();
return app.exec();
}
The Qt blog has a very good write-up on optimizing Qt3D applications for low-end hardware.
https://blog.qt.io/blog/2019/04/02/optimizing-real-time-3d-entry-level-hardware/
I use fitInView() function to view the image pixels, but image pixels exists offset, how to solve this problem?
void QtGraphicsView::setViewRect(const QRectF &rect)
{
if (m_viewRect == rect)
return;
m_viewRect = rect;
fitInView(rect);
}
void QtGraphicsView::scaleView(qreal factor)
{
const QSizeF &size = m_viewRect.size() / factor;
if (size.width() <= 0 || size.height() <= 0
|| size.width() >= sceneRect().width()
|| size.height() >= sceneRect().height())
return;
const QSizeF &dsize = m_viewRect.size() - size;
const QPointF &topLeft = m_viewRect.topLeft()
+ QPointF(dsize.width() / 2, dsize.height() / 2);
setViewRect(QRectF(topLeft, size).toRect());
}
I've implemented sorting algorithm using openCL. Its using one work group per array to sort (arrays are connected in __global float *array, all have the same size).
Im testing results using 200 random arrays and result are deterministic.
With one parameter, its working correctly till array size exceeds of array 768
With two parameters, its working correctly till arrays size exceeds 768
With three parameters, its working correctly till arrays size exceeds 317
What could be the reason of correct processing of just 768 (CL_KERNEL_WORK_GROUP_SIZE returns 1024 elements). Is it some memory constraints? What is the best way of invastigation such issue?
Gpu specs (4th answer):
Kernel code below:
__kernel void assort(
__global float *array,
__local float *currentOutput,
__local float *stimulations,
__local int *noOfValuesAdded,
__local float *addedValue,
__local float *positionToInsert,
__local int *activatedIdx,
__local float *range,
int size
) {
int id = get_local_id(0);
int gid = get_group_id(0);
if (id == 0)
{
if (array[gid*size]<array[gid*size+1])
{
currentOutput[0] = array[gid*size];
currentOutput[1] = array[gid*size + 1];
}
else
{
currentOutput[1] = array[gid*size];
currentOutput[0] = array[gid*size + 1];
}
noOfValuesAdded[0] = 2;
}
barrier(CLK_LOCAL_MEM_FENCE);
for (int i = 2; i < size; i++)
{
int maxIdx = noOfValuesAdded[0] - 1;
if (id == 0)
{
addedValue[0] = array[gid*size + i];
positionToInsert[0] = -100.0f;
activatedIdx[0] = -2;
range[0] = currentOutput[maxIdx] - currentOutput[0];
}
barrier(CLK_LOCAL_MEM_FENCE);
if (id < noOfValuesAdded[0])
{
if (id == 0)
{
stimulations[id] = (currentOutput[maxIdx] - addedValue[0]) / range[0];
float stimulation = stimulations[id];
if ( fabs(stimulation - 1.0f) < 0.000001)
activatedIdx[0] = 0;
else if (stimulation > 1.0f)
{
activatedIdx[0] = -1;
}
}
else if (id == maxIdx)
{
stimulations[maxIdx] = (addedValue[0] - currentOutput[0]) / range[0];
float stimulations = (addedValue[0] - currentOutput[0]) / range[0];
if ( fabs(stimulations - 1.0f) < 0.000001 )
activatedIdx[0] = maxIdx;
else
if (stimulations > 1)
activatedIdx[0] = maxIdx + 1;
}
else
{
stimulations[id] = 1.0f - (fabs((currentOutput[id] - addedValue[0])) / range[0]);
if ( fabs(stimulations[id] - 1.0f) < 0.000001)
activatedIdx[0] = id;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (activatedIdx[0] == -2 && id < noOfValuesAdded[0])
{
if (noOfValuesAdded[0] == 2)
{
positionToInsert[0] = 0.9f;
}
else if (id != 0 &&
id != maxIdx &&
stimulations[id] >= stimulations[(id - 1)] &&
stimulations[id] >= stimulations[(id + 1)] )
{
if ((1.0f - (fabs(currentOutput[(id - 1)] - currentOutput[id]) / range[0]) ) < stimulations[(id - 1)])
positionToInsert[0] = (float)id - 0.1f;
else
positionToInsert[0] = (float)id + 0.9f;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (activatedIdx[0] == -2)
{
if (id == 0 && positionToInsert[0] < -90.0f) // default value maintained
{
if (stimulations[0] > stimulations[1])
positionToInsert[0] = 0.9f;
else
positionToInsert[0] = (float)maxIdx - 0.1f;
}
}
else
{
if (activatedIdx[0] == -1)
positionToInsert[0] = -0.1f;
else if (activatedIdx[0] == (maxIdx + 1))
{
positionToInsert[0] = (float)maxIdx + 0.9f;
}
else
{
currentOutput[activatedIdx[0]] = addedValue[0];
}
}
barrier(CLK_LOCAL_MEM_FENCE);
if (positionToInsert[0] > -50.0f) // default value changed
{
float temp = 0.0f;
if ((float)id>positionToInsert[0])
{
temp = currentOutput[id];
currentOutput[id + 1] = temp;
}
barrier(CLK_LOCAL_MEM_FENCE);
if ((float)id > positionToInsert[0])
{
temp = currentOutput[id];
}
barrier(CLK_LOCAL_MEM_FENCE);
if (id == round(positionToInsert[0]))
{
currentOutput[id] = addedValue[0];
noOfValuesAdded[0] = noOfValuesAdded[0] + 1;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
}
barrier(CLK_LOCAL_MEM_FENCE);
array[gid*size + id] = currentOutput[id];
return;
}
I'm stuck trying to make a analog clock where the hour hand is following the minute hand.
Tried all i can think off but the ratio is off.
Dragging the minute hand works correctly so i think it's the way i calculate the ratio of which the hour hand should rotate.
This is my most recent try of many...
Code:
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
CGFloat oldAngleInRadians = M_PI_2;
- (void)rotateNode:(SKSpriteNode *)node forTouch:(UITouch *)touch
{
if ([node isEqual:dial]) return;
CGPoint positionInDial = [touch locationInNode:dial];
float deltaY = positionInDial.y - minuteIndicator.anchorPoint.y;
float deltaX = positionInDial.x - minuteIndicator.anchorPoint.x;
CGFloat angleInRadians = atan2f(deltaY, deltaX);
// Minute hand rotation in this case
[node runAction:[SKAction rotateToAngle:angleInRadians - (M_PI / 2) duration:0]];
if (self.difficulty == kMediumDifficulty ) {
[self updateHourhandAngleWithNewAngle:angleInRadians oldAngle:oldAngleInRadians];
oldAngleInRadians = angleInRadians;
}
}
#pragma mark - Rotate hour hand with minute hand
- (void)updateHourhandAngleWithNewAngle:(CGFloat)newAngle oldAngle:(CGFloat)oldAngle
{
double newAngleDeg = RADIANS_TO_DEGREES(newAngle);
double oldAngleDeg = RADIANS_TO_DEGREES(oldAngle);
double differenceDeg = 0;
if (newAngleDeg > 0 && oldAngleDeg < 0) {
differenceDeg = oldAngleDeg - ( - 1 * newAngleDeg );
} else if (newAngleDeg < 0 && oldAngleDeg > 0) {
differenceDeg = fabsf(newAngleDeg) - oldAngleDeg;
} else {
differenceDeg = newAngleDeg - oldAngleDeg;
}
[hourIndicator runAction:[SKAction rotateByAngle:DEGREES_TO_RADIANS((differenceDeg / 12.0f)) duration:0]];
}
Not sure if I understood your problem correctly, but this is how I would make an analog clock using SpriteKit:
// MyScene.m
- (void) didMoveToView:(SKView *)view
{
[self.clock didEnterScene];
}
- (void) update:(CFTimeInterval)currentTime
{
CGFloat dt = 0.0;
if (self.lastUpdateInterval > 0)
{
dt = currentTime - self.lastUpdateInterval;
}
[self.clock update:dt];
self.lastUpdateInterval = currentTime;
}
// ClockNode.m
- (void) didEnterScene
{
SKSpriteNode* secHand = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(2, 100)];
SKSpriteNode* minHand = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(5, 100)];
SKSpriteNode* hourHand = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(10, 70)];
secHand.position = CGPointMake(CGRectGetMidX(self.scene.frame), CGRectGetMidY(self.scene.frame));
minHand.position = secHand.position;
hourHand.position = secHand.position;
secHand.anchorPoint = CGPointMake(0.5, 1.0);
minHand.anchorPoint = CGPointMake(0.5, 1.0);
hourHand.anchorPoint = CGPointMake(0.5, 1.0);
[self addChild:secHand];
[self addChild:minHand];
[self addChild:hourHand];
_secHand = secHand;
_minHand = minHand;
_hourHand = hourHand;
_secHand.zRotation = M_PI;
_minHand.zRotation = M_PI;
_hourHand.zRotation = M_PI;
_msec = 0.0f;
_sec = 0.0f;
_min = 0.0f;
_hour = 0.0f;
}
- (void) update:(CGFloat)dt
{
_msec += dt;
if (_msec >= 1)
{
_msec -= 1;
_sec += 1;
_secHand.zRotation -= 2*M_PI / 60;
}
if (_sec >= 60)
{
_sec -= 60;
_min += 1;
_minHand.zRotation -= 2*M_PI / 60;
}
if (_min >= 60)
{
_min -= 60;
_hour += 1;
_hourHand.zRotation -= 2*M_PI / 12;
}
if (_hour >= 12)
{
_hour -= 12;
}
}