I am trying to load several big models in code and show the scenes. Generally, it is taking long time for loading and showing scene on screen as it need to extract lot of resources from a pod model.
So, i thought of populate the first scene in main thread and remaining others in a separate thread. But, it is crashing when i move my part code into separate thread. Here is my sample code:
-(void) loadFirstScene
{
CC3PODResourceNode* podRezNode = [CC3PODResourceNode nodeWithName: #"FirstModel"];
podRezNode.resource = [IntroducingPODResource resourceFromFile: #"FirstModel.pod"];
podRezNode.shouldCullBackFaces = NO;
podRezNode.location = cc3v(0.0, -10.0, 0.2);
podRezNode.isTouchEnabled = YES;
[self addChild: podRezNode];
[NSThread detachNewThreadSelector:#selector(loadScenesInThread) toTarget:self
withObject:nil];
}
// Crashing if i add the below function in separate thread or background thread
-(void) loadScenesInThread
{
CC3PODResourceNode* podRezNode = [CC3PODResourceNode nodeWithName: #"SecondModel"];
podRezNode.resource = [IntroducingPODResource resourceFromFile: #"SecondModel.pod"];
podRezNode.shouldCullBackFaces = NO;
podRezNode.location = cc3v(0.0, -10.0, -5.0);
podRezNode.isTouchEnabled = YES;
[self addChild: podRezNode];
podRezNode = [CC3PODResourceNode nodeWithName: #"ThirdModel"];
podRezNode.resource = [IntroducingPODResource resourceFromFile: #"ThirdModel.pod"];
podRezNode.shouldCullBackFaces = NO;
podRezNode.location = cc3v(0.0, -10.0, -5.0);
podRezNode.isTouchEnabled = YES;
[self addChild: podRezNode];
// .. do more
}
Could someone guide me how would i handle such situation?
You can not create or access OpenGL resources in a thread that's different from the thread the OpenGL context was created on. Your only option is to use any "async" method cocos2d offers, or example for loading textures.
Related
Qt: 5.14.1
SDL: 2.0.12
OS: Windows 10
I'm working on a video player and I'm using Qt for UI and SDL for rendering frames.
I created the SDL window by passing my rendering widget's (inside a layout) winId() handle.
This works perfectly when I start a non-threaded Play().
However, this causes some issues with playback when resizing or moving the app window. Nothing serious, but since the play code is non threaded my frame queues fill-up which then causes the video to speed up until it catches to the audio.
I solved that by putting my play code inside Win32 thread created with CreateThread function.
Now when I move window the video continues to play as intended, but when resizing the app, rendering widget will stop refreshing the widget and only the last displayed frame before resize event will be shown.
I can confirm that video is still running and correct frames are still being displayed. The displayed image can even be resized, but its never refreshed.
The similar thing happens when I was testing Qt threads with SDL. Consider this code
class TestThread: public QThread
{
public:
TestThread(QObject *parent = NULL) : QThread(parent)
{
}
void run() override
{
for (;;)
{
SDL_Delay(1000/60);
// Basic square bouncing animation
SDL_Rect spos;
spos.h = 100;
spos.w = 100;
spos.y = 100;
spos.x = position;
SDL_SetRenderDrawColor(RendererRef, 0, 0, 0, 255);
SDL_RenderFillRect(RendererRef, 0);
SDL_SetRenderDrawColor(RendererRef, 0xFF, 0x0, 0x0, 0xFF);
SDL_RenderFillRect(RendererRef, &spos);
SDL_RenderPresent(RendererRef);
if (position >= 500)
dir = 0;
else if (position <= 0)
dir = 1;
if (dir)
position += 5;
else
position -= 5;
}
}
};
// a call from Init SDL and Start Thread button
...
// create new SDL borderless resizible window.
WindowRef = SDL_CreateWindow("test",10,10,1280,800,SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS);
// create and start thread
test_thread = new TestThread();
test_thread->start();
...
This will create a separate window from the Qt app window and will start rendering a bouncy square. However if any resize event occurs in the Qt app, the rendering context will be lost and the the same thing that happens in my video player will happen here.
I also found out that If I remove SDL_RenderPresent function from the Thread object and put it in a Main Qt Window, the rendering will continue after resize event. However this has proved as completely unreliable and will sometimes completely freeze my app.
I also can't figure out why my completely separate SDL window and renderer still freezes on resize.
I presume there is a clash somewhere with SDL renderer/window and Qt's drawing stuff, but I'm at a loss here.
Also, it's only the resize stuff. Everything else works.
Thanks.
Answer:
SDL_Renderer needs to be destroyed and recreated on window resize as well as any SDL_Texture created with previous renderer.
The same thing will happen even without qt.
However, I think this is just a workaround and not a real solution.
A simple code to recreate the issue.
int position = 0;
int dir = 0;
SDL_Window *window = NULL;
SDL_Renderer *sdlRenderer_ = NULL;
DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
for (;;)
{
SDL_Delay(1000/60);
// Basic square bouncing animation
SDL_Rect spos;
spos.h = 100;
spos.w = 100;
spos.y = 100;
spos.x = position;
SDL_SetRenderDrawColor(sdlRenderer_, 0, 0, 0, 255);
SDL_RenderFillRect(sdlRenderer_, 0);
SDL_SetRenderDrawColor(sdlRenderer_, 0xFF, 0x0, 0x0, 0xFF);
SDL_RenderFillRect(sdlRenderer_, &spos);
SDL_RenderPresent(sdlRenderer_);
if (position >= 500)
dir = 0;
else if (position <= 0)
dir = 1;
if (dir)
position += 5;
else
position -= 5;
}
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine,_In_ int nCmdShow)
{
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("test",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,600,600,SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (!window)
printf("Unable to create window");
sdlRenderer_ = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED |SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
if (!sdlRenderer_)
printf("Unable to create renderer");
HANDLE playHandle = CreateThread(0, 0, MyThreadFunction, 0, 0, 0);
if (playHandle == NULL)
{
return 0;
}
SDL_Event e;
while(1)
{
SDL_PollEvent(&e);
if (e.type == SDL_WINDOWEVENT )
{
switch( e.window.event )
{
case SDL_WINDOWEVENT_SIZE_CHANGED:
int mWidth = e.window.data1;
int mHeight = e.window.data2;
SDL_DestroyRenderer(sdlRenderer_); // stops rendering on resize if commented out
sdlRenderer_ = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED |SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
break;
}
}
}
return 0;
}
EDIT:
Real solution.
Renderer doesn't need to be recreated. Seperate the Qt code from SDL main thread by making a seperate thread. Create all SDL stuff in that thread because SDL_Renderer needs to be created in a thread that handles SDL events.
Use SDL_PushEvent to signal this thread to render to screen.
This way textures don't need to be recreated.
I have a Qwt plot defined in a class method:
plot = new QwtPlot();
const int margin = 5;
plot->setContentsMargins( margin, margin, margin, 0 );
plot->setTitle( "Support polygon" );
plot->setCanvasBackground( Qt::white );
plot->setAxisScale( QwtPlot::yLeft, -0.8,0.8 );
plot->setAxisScale( QwtPlot::xBottom, -0.8,0.8 );
QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight);
layout->addWidget(plot);
setLayout(layout);
curve_ = new QwtPlotCurve();
curve_->attach( plot );
xData = new double[4];
yData = new double[4];
QTimer *replotTimer_ = new QTimer(this);
connect(replotTimer_, SIGNAL(timeout()), this, SLOT(updateMe_()));
replotTimer_->start(100);
the data is updated when the timer calls the callback function updateMe_() and the plot should be updated
void Support_polygon::updateMe_()
{
curve_->setRawSamples(xData,yData,4);
plot->replot();
}
xData and yData are also being modified in a thread, whose callback function is:
void Support_polygon::callback_()
{
msg_mutex.lock();
for (size_t ii=0; ii<msg.contacts.size(); ii++)
{
xData[ii] = 1.4f*float(std::rand())/float(RAND_MAX)-0.7;
yData[ii] = 1.4f*float(std::rand())/float(RAND_MAX)-0.7;
}
msg_mutex.unlock();
}
(right now I'm only putting random numbers, but when this works the data will be passed by a ROS message, that's why is in a different thread)
The problem is that the plot is never updated. As if replot() is never called. I tested and all callback functions are being called.
Surprisingly, the plot gets updated if I resize the window... if I keep re-sizing, the plot gets updated while I do it.
The replot call is being done on the main thread by the timer timeout signal. I don't understand what is going on.
Even though in the documentation it says that setAutoReplot is not recommended and that performance wise is better to use replot, I tested setting setAutoReplot to true
plot->setAutoReplot( true );
removed the replot call in updateMe_(), and everything works.
But to me this seems like a bug.
Note: I'm using Qwt 6.1.2.
note: after many more issues with qwt I switched to qtcustomplot. Only one header and one cpp file. My two cents in case anyone is looking for a substitute.
So I'm coming from a bit of a processing background (albiet not much) where I'm used to working with a draw loop that runs over and over when the app is running.
I can't seem to find something similar in xcode and C4. Is there anything like that anyone can suggest?
What I'm basically doing is just creating a bouncing ball app using C4 vectors as properties in a custom class
Here is my custom class Header/Implementation file:
#interface splitSquare : C4Shape
#property C4Vector *accel;
#property C4Vector *velocity;
#property C4Vector *location;
-(id)initWithPoint:(CGPoint)point;
#end
-(id)initWithPoint:(CGPoint)point{
self = [super init];
if (self){
CGRect frame = CGRectMake(0, 0, 50, 50);
[self ellipse:frame];
self.lineWidth = 0.0f;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
_location = [C4Vector vectorWithX:point.x Y:point.y Z:0.0f];
self.origin = _location.CGPoint;
_accel = [C4Vector vectorWithX:0.04f Y:0.05f Z:0.0f];
_velocity = [C4Vector vectorWithX:0.004f Y:0.0005f Z:0.0f];
C4Log(#"The current value of velocity x is %f and y is %f", _velocity.x, _velocity.y);
}
I'm doing something pretty simple (adding accel to velocity and velocity to location, then assign location to the shapes origin). In the main C4WorkSpace I have this code:
#interface C4WorkSpace()
-(void)updateVectors;
#end
#implementation C4WorkSpace
{
splitSquare *testShape;
C4Timer *timer;
}
-(void)setup {
testShape = [[splitSquare alloc] initWithPoint:point2];
[self.canvas addShape:testShape];
testShape.animationDuration = 0.25f;
timer = [C4Timer automaticTimerWithInterval:0.25f target:self method:#"updateVectors" repeats:YES];
}
-(void)updateVectors{
//accel add to vel, vel added to location
[testShape.velocity add:testShape.accel];
[testShape.location add:testShape.velocity];
testShape.origin = testShape.location.CGPoint;
testShape.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
}
#end
So this is what I'm doing now with a C4 timer getting called, but I feel like there has to be a more elegant way to do this. Right now the animation is jumpy and although it doesn't throw an error it doesn't seem to work properly (every time I run the iOS simulator it runs for a second then jumps back into xcode and shows me the current thread).
Any/all suggestions would be awesome thanks!
You're actually right on track with using a timer to create an update loop. Instead of having an entire update for the screen, C4 updates only the content it needs to update at any given time. This is why there's no draw loop like in other apis. A major reason for this is to be lighter on system resources (i.e. less drawing / less updating = longer battery life for mobile devices).
To address your issues above, I've slightly modified the code you sent over.
Here's the splitSquare header:
#import "C4Shape.h"
#interface splitSquare : C4Shape
#property C4Vector *accel;
#property C4Vector *velocity;
#property C4Vector *location;
-(id)initWithPoint:(CGPoint)point;
-(void)beginUpdating;
#end
NOTE: I added a beginUpdating method to the header.
Here's the splitSquare implementation:
#import "splitSquare.h"
#implementation splitSquare {
C4Timer *updateVectorTimer;
}
-(id)initWithPoint:(CGPoint)point{
self = [super init];
if (self){
CGRect frame = CGRectMake(0, 0, 50, 50);
[self ellipse:frame];
self.lineWidth = 0.0f;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
_location = [C4Vector vectorWithX:point.x Y:point.y Z:0.0f];
self.origin = _location.CGPoint;
_accel = [C4Vector vectorWithX:0.04f Y:0.05f Z:0.0f];
_velocity = [C4Vector vectorWithX:0.004f Y:0.0005f Z:0.0f];
C4Log(#"The current value of velocity x is %f and y is %f",
_velocity.x,
_velocity.y);
}
return self;
}
-(void)beginUpdating {
updateVectorTimer = [C4Timer automaticTimerWithInterval:1/60.0f
target:self
method:#"updateVectors"
repeats:YES];
}
-(void)updateVectors{
//accel add to vel, vel added to location
[self.velocity add:self.accel];
[self.location add:self.velocity];
self.origin = self.location.CGPoint;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:100]/100.0f
green:[C4Math randomInt:100]/100.0f
blue:0.5f
alpha:1.0f];
}
#end
NOTE: I added the timer directly to the splitSquare object.
The main thing for making your animation smoother happens in the beginUpdating method when you create the update timer.
Originally, you were animating for 0.25 seconds before updating and triggering the timer every 0.25 seconds as well. I've changed the animation duration so that it is immediate (0.0f seconds).
I then changed the update timer to run at 60fps by triggering the timer at '1/60.0f' seconds so that the animation is smoother.
I made an alternative implementation (just to show that it can be done this way as well) by adding the animation and timer to the splitSquare method itself. The C4WorkSpace changed to the following:
#import "C4WorkSpace.h"
#import "splitSquare.h"
#implementation C4WorkSpace {
splitSquare *testShape;
}
-(void)setup {
testShape = [[splitSquare alloc] initWithPoint:CGPointMake(100,100)];
[self.canvas addShape:testShape];
[testShape beginUpdating];
}
#end
Instead of managing the animation of the object itself, the workspace simply starts tells the object to beginUpdating.
A bit about what's going on.
One of the main differences, as you know, is that there is no default draw loop. Part of the reason for this is that as the developer you're almost never really dealing with the actual drawing/rendering of objects to the screen. When you create a visual object and add that to the canvas, the rendering is done by the system itself so that drawing is much more efficient on system resources. This is the ideal way of dealing with media not only in C4 but also in iOS programming.
Not having a draw loop is definitely different and takes some getting used to. But, it's really quite a nice way of working.
In essence, the draw-loop in other apis gets triggered by something like a timer every 30 or 60 seconds. So, adding a timer the way you did to update your shape is exactly what you want to do if you need loop-like functionality.
The nice thing about NOT having a timer in C4 is that you can simply add one to your workspace to control the firing of updates. BUT an even nicer thing is that you can add timers to individual objects so that they can update themselves. This means that you can also have multiple timers in multiple objects updating at different times / delays to create a more dynamic set of actions than you could ever get with only a single running loop.
I have an old code that uses,
Rect r;
GetPortBounds(some_bitmap,&r);
PixMapHandle somehandle = GetGWorldPixMap(some_bitmap);
if(LockPixels(somehandle)){
TPixel *data = (TPixel *) GetPixBaseAddr(somehandle);
long row_bytes = GetPixRowBytes(somehandle);
// doing something
UnlockPixels(somehandle);
}
Can anyone help me with the replacement code in quartz 2d
To modify a bitmap with Quartz you can initialize a CGContextRef with the image and draw to that context with CGContextDraw... routines.
(I wrote the following sample code for a NSView subclass. It's a bit inefficient. If you use the code, separate the stuff you can keep around in iVars.)
- (void)drawRect:(NSRect)dirtyRect
{
//Load an image ...
NSImage* image = [[NSImage alloc] initWithContentsOfFile:#"/Library/Desktop Pictures/Grass Blades.jpg"];
CGImageRef testImage = [[[image representations] objectAtIndex:0] CGImage];
[image release];
CGDataProviderRef dataProvider = CGImageGetDataProvider(testImage);
//... and retrieve its pixel data
CFDataRef imageData = CGDataProviderCopyData(dataProvider);
void* pixels = (void*)CFDataGetBytePtr(imageData);
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
//Init a quartz context that uses the pixel data memory as buffer
CGContextRef drawContext = CGBitmapContextCreate(pixels, CGImageGetWidth(testImage), CGImageGetHeight(testImage), CGImageGetBitsPerComponent(testImage), CGImageGetBytesPerRow(testImage), colorspace, CGImageGetBitmapInfo(testImage));
CGContextSetRGBFillColor(drawContext, 0.8, 0.8, 0.8, 1.0);
//Do something with the newly created context
CGContextFillRect(drawContext, CGRectMake(20.0, 20.0, 200.0, 200.0));
CGColorSpaceRelease(colorspace);
CGImageRef finalImage = CGBitmapContextCreateImage(drawContext);
//Draw the modified image to the screen
CGContextDrawImage([[NSGraphicsContext currentContext] graphicsPort], dirtyRect, finalImage);
CFRelease(imageData);
CGImageRelease(finalImage);
CGContextRelease(drawContext);
}
I am new in cocos3d but i know cocos2d. i want to create 3d box dynamically. so what i did inside cc3layer is
-(void) initializeControls {
[self schedule:#selector(create_box:) interval:2 ];
}
-(void)create_box:(id)sender{
[self unschedule:#selector(mov_cel:)];
[[testWorld sharedcontescWorld] world_create_box];
}
and in cc3world class is
static testWorld *_sharedcontescWorld=nil;
+(testWorld *)sharedcontescWorld{
#synchronized([testWorld class]){
if (!_sharedcontescWorld)
[self alloc];
return _sharedcontescWorld;
}return nil;
}
+(id)alloc{
#synchronized([testWorld class]) {
_sharedcontescWorld = [super alloc];
return _sharedcontescWorld;
}return nil;
}
-(void) world_create_box{
int minx=-50;
int maxx=50;
float posx=(float)(minx+arc4random()%maxx);
CC3MeshNode* aNode;
aNode = [CC3BoxNode nodeWithName: #"Simple box"];
CC3BoundingBox bBox;
bBox.minimum = cc3v(-10.0, -10.0, -10.0);
bBox.maximum = cc3v( 10.0, 10.0, 10.0);
[aNode populateAsSolidBox: bBox];
[aNode setLocation:cc3v(posx,0,0)];
aNode.material = [CC3Material material];
[self addChild:aNode];
id move3d=[CC3MoveTo actionWithDuration:1 moveTo:cc3v(posx,0,100)];
id remove=[CCCallFuncND actionWithTarget:self selector:#selector(removeObj:)];
[aNode runAction:[CCSequence actions:move3d,remove,nil]];
}
but it doesn't work......can anyone help me?
Do you have a camera and light set up as well?
Without camera info it's a hard to tell, but I think you might either not be looking at the cube (camera missing or aimed wrong), or the cube is too big and you're inside it.
Try it with a camera and light, and some more modest sizes.
Here's some example code that worked for me in the cocos3d XCode project template:
// Create the camera, place it back a bit, and add it to the world
CC3Camera* cam = [CC3Camera nodeWithName: #"Camera"];
cam.location = cc3v( 0.0, 0.0, 10.0 );
[self addChild: cam];
// Create a light, place it back and to the left at a specific
// position (not just directional lighting), and add it to the world
CC3Light* lamp = [CC3Light nodeWithName: #"Lamp"];
lamp.location = cc3v( -2.0, 0.0, 0.0 );
lamp.isDirectionalOnly = NO;
[cam addChild: lamp];
float maxx=5.0;
float posx=CCRANDOM_MINUS1_1()*maxx;
CC3BoxNode* aNode;
aNode = [CC3BoxNode nodeWithName: #"Simple box"];
CC3BoundingBox bBox;
bBox.minimum = cc3v(-1.0, -1.0, -1.0);
bBox.maximum = cc3v( 1.0, 1.0, 1.0);
[aNode populateAsSolidBox: bBox];
[aNode setLocation:cc3v(posx,0.0f,-5.0f)];
aNode.material = [CC3Material material];
[self addChild:aNode];
id move3d=[CC3MoveTo actionWithDuration:1.0f moveTo:cc3v(posx*-1.0,0.0f,-5.0f)];
// id remove=[CCCallFuncND actionWithTarget:self selector:#selector(removeChild:)];
[aNode runAction:[CCSequence actions:move3d,/*remove,*/nil]];
Notes:
cocos3d readme has instructions to install the XCode project templates, if you haven't yet.
I replaced all the "Hello World" object code in the template with this stuff (note it's the same camera and light code/comments); you can leave it but you'll have some words in front of your box.
Changed CC3MeshNode to CC3BoxNode for clarity, but totally CC3MeshNode works too.
Replaced arc4random() with the cocos2d helper function
CCRANDOM_MINUS1_1() just for ease of reading (and to share this
cocos2d gem).
Commented out the CCFuncCallND because I didn't have a removeObj function. Hope you do. ;)
Hope that helps.
I have not started with cocos3d but I have found this code on stackoverflow that works
CC3BoundingBox bounds = makeBounds(9.5, 5.0, 4.0, 0, 0, 0);
CC3MeshNode *cube = [[CC3MeshNode alloc] init];
[cube populateAsSolidBox:bounds];
There is a CC3BoxNode class to help you do that. Eg:
CC3BoxNode *box = [CC3BoxNode nodeWithName:#"MyBox"];
[box populateAsSolidBox:CC3BoxMake(-1, -1, -1, 1, 1, 1)];
[self addChild:box];
This would create a 2x2x2 box with its origin point in its absolute center.
Hope that helps.