When I try to blend with a color with an alpha of 0.5 or below, metal seemingly discards the color like it has an alpha of 0. When I set the alpha to 0.51, I can see it fine. When I set it to 0.5, it's invisible. Here is a simple implementation of the issue:
#implementation Renderer
{
id <MTLDevice> _device;
id <MTLCommandQueue> _commandQueue;
id<MTLLibrary> defaultLibrary;
id <MTLBuffer> _vertexBuffer;
id <MTLBuffer> _indexBuffer;
id <MTLRenderPipelineState> _pipelineState;
}
-(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view;
{
self = [super init];
if(self)
{
_device = view.device;
view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
_commandQueue = [_device newCommandQueue];
[self _createRenderObject];
}
return self;
}
-(void)_createRenderObject
{
Vertex verts[4] = {
simd_make_float2(-0.5f, -0.5f),
simd_make_float2(0.5f,-0.5f),
simd_make_float2(0.5f,0.5f)
};
uint16_t indices[3] = {0,1,2};
_vertexBuffer = [_device newBufferWithBytes:&verts length:sizeof(verts) options:MTLResourceStorageModeShared];
_indexBuffer = [_device newBufferWithBytes:&indices length:sizeof(indices) options:MTLResourceStorageModeShared];
// Create Pipeline State
defaultLibrary = [_device newDefaultLibrary];
MTLRenderPipelineDescriptor *pd = [[MTLRenderPipelineDescriptor alloc] init];
pd.vertexFunction = [defaultLibrary newFunctionWithName: #"VertShader"];
pd.fragmentFunction = [defaultLibrary newFunctionWithName: #"FragShader"];
pd.alphaToCoverageEnabled = YES;
MTLRenderPipelineColorAttachmentDescriptor *cad = pd.colorAttachments[0];
cad.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
cad.blendingEnabled = YES;
cad.alphaBlendOperation = MTLBlendOperationAdd;
cad.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
cad.destinationAlphaBlendFactor = MTLBlendFactorDestinationAlpha;
cad.rgbBlendOperation = MTLBlendOperationAdd;
cad.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
NSError *error = NULL;
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pd error:&error];
}
- (void)drawInMTKView:(nonnull MTKView *)view
{
id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
id <MTLRenderCommandEncoder> renderEncoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise];
[renderEncoder setCullMode:MTLCullModeBack];
[renderEncoder setRenderPipelineState:_pipelineState];
[renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:0];
[renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:3
indexType:MTLIndexTypeUInt16
indexBuffer:_indexBuffer
indexBufferOffset:0];
[renderEncoder endEncoding];
[commandBuffer presentDrawable:view.currentDrawable];
[commandBuffer commit];
}
#end
Shader.metal:
typedef struct {
float4 position [[position]];
} VertexOut;
vertex VertexOut
VertShader(const uint vertexID [[vertex_id]],
constant Vertex *vertices [[buffer(0)]])
{
VertexOut out;
Vertex v = vertices[vertexID];
out.position = (float4){v.position.x,v.position.y,0,1};
return out;
}
fragment half4
FragShader(VertexOut in [[stage_in]])
{
return half4(1,1,1,0.50f);
}
With that code, specifically the FragShader having 0.50f as the alpha value, I get a blank canvas:
If I change the alpha value to 0.51f:
fragment half4
FragShader(VertexOut in [[stage_in]])
{
return half4(1,1,1,0.51f);
}
I then get this:
Any help is appreciated!
Solved. The problem was that alphaToCoverageEnabled was set to true, while the render target texture type was NOT MTLTextureType2DMultisample. It appears the two work in tandem, but it's beyond my understanding how.
If not using multi-sampling, set alphaToCoverageEnabled to false.
Otherwise, make sure the render target is of type MTLTextureType2DMultisample.
If using MTKView, set the render target texture type by setting the sampleCount on the MTKView object:
_view = (MTKView *)self.view;
_view.sampleCount = 2;
and the render pipeline descriptor of the pipeline state:
MTLRenderPipelineDescriptor *pd = [[MTLRenderPipelineDescriptor alloc] init];
pd.sampleCount = 2;
Related
I am working on a game that incorporates touch detection with skShapenodes that are circles, and can not find a good method to check if they are touching. Code for the player class is below
-(void)SpawnPlayer
{
_player = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0,0, 15, 0, M_PI*2, YES);
_player.path = myPath;
_player.lineWidth = 1.0;
_player.fillColor = [SKColor whiteColor];
_player.strokeColor = [SKColor whiteColor];
_player.glowWidth = 0.5;
_location = CGPointMake(375, 400);
_player.position = CGPointMake(375, 400);
_player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:0.5];
_player.physicsBody.dynamic = NO;
_player.physicsBody.categoryBitMask = playerCategory;
_player.physicsBody.contactTestBitMask = enemyCategory;
_player.physicsBody.collisionBitMask = 0;
[self addChild:_player];
}
The enemy code is similar, with the exception that its bitmask and testbitmask are switched.
Think of it as this:
The categoryBitMask specifies the type of body a node is.
The collisionBitMask specifies which type of bodies it can collide with.
The contactTestBitMask specifies which type of contactTestBitMasks will interact with it.
e.g. to detect contact between player and enemy, which have different categoryBitMasks, their contactTestBitMasks should be:
_player.physicsBody.categoryBitMask = playerCategory;
_player.physicsBody.contactTestBitMask = playerCategory | enemyCategory ;
For enemy:
_enemy.physicsBody.categoryBitMask = enemyCategory;
_enemy.physicsBody.contactTestBitMask = playerCategory | enemyCategory ;
now you can handle what to do on contact in didBeginContact method
I'm trying to figure out how to reposition the MKMap region programmatically so that my annotation (automatically selected when the map loads) will all fit centered.
Current Result: https://www.evernote.com/shard/s46/sh/7c7d2ed8-203c-4878-af8c-83ff77ad7b21/ce7786acdf66b0782fc689b72d1b67e7
Desired Result: https://www.evernote.com/shard/s46/sh/21fb0eab-d5c4-4e6d-b05b-322e7dcce8ab/ab816f2a24f11b9c9e15bf55ac648f72
I have tried to reposition everything in - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views but that didn't work. Is there a better approach?
// here is viewWillAppear logic
[self.mapView removeAnnotations:self.mapView.annotations];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLGeocodeCompletionHandler completionHandler = ^(NSArray *placemarks, NSError *error) {
if (error) {
EPBLog(#"error finding placemarks: %#", [error localizedDescription]);
} else {
if (placemarks) {
[placemarks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CLPlacemark *placemark = (CLPlacemark *)obj;
if ([placemark.country isEqualToString:#"United States"]) {
EPBAnnotation *annotation = [EPBAnnotation annotationWithCoordinate:placemark.location.coordinate];
annotation.title = self.locationObj.locationName;
annotation.subtitle = self.locationObj.locationAddress;
[self.mapView addAnnotation:annotation];
self.mapView.selectedAnnotations = #[annotation];
[self.mapView setCenterCoordinate:placemark.location.coordinate];
/**
* #todo
* MOVE THIS OUTTA HERE
*/
MKCoordinateRegion region = {{0.0f, 0.0f}, {0.0f, 0.0f}};
region.center = placemark.location.coordinate;
region.span.longitudeDelta = 0.003f;
region.span.latitudeDelta = 0.003f;
[self.mapView setRegion:region animated:YES];
[self.mapView regionThatFits:region];
*stop = YES;
}
}];
}
}
};
[geocoder geocodeAddressString:self.locationObj.locationAddress completionHandler:completionHandler];
Following method will fit the map on region to show all annotations. You can call this method in Map's didAddAnnotations method.
- (void)zoomToFitMapAnnotations {
if ([mMapView.annotations count] == 0) return;
int i = 0;
MKMapPoint points[[mMapView.annotations count]];
//build array of annotation points
for (id<MKAnnotation> annotation in [mMapView annotations]){
points[i++] = MKMapPointForCoordinate(annotation.coordinate);
}
MKPolygon *poly = [MKPolygon polygonWithPoints:points count:i];
[mMapView setRegion:MKCoordinateRegionForMapRect([poly boundingMapRect]) animated:YES];
}
Howevcer you should see if you want to add user location annotation in visible area also. If you don't then in loop check if current annotation is MkUserLocation and don't add it's points in points array.
if ([annotation isKindOfClass:[MKUserLocation class]]) {
continue:
}
Now if you wanted an Annotation to be in center and selected automatically then do this
annotation.coordinate=mMapView.centerCoordinate;
[mMapView selectAnnotation:annotation animated:YES];
I have a UIWebView that loads text from an htmlString.
I need when the user selects a part of the text and presses a button, i will be capable of extracting it in order to use it elsewhere, so i am using this code :
// The JS File
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"HighlightedString" ofType:#"js" inDirectory:#""];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
NSString *jsString = [[NSMutableString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];
[WebV2 stringByEvaluatingJavaScriptFromString:jsString];
// The JS Function
NSString *startSearch = [NSString stringWithFormat:#"getHighlightedString()"];
[WebV2 stringByEvaluatingJavaScriptFromString:startSearch];
NSString *selectedText = [NSString stringWithFormat:#"selectedText"];
NSString * highlightedString = [WebV2 stringByEvaluatingJavaScriptFromString:selectedText];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Highlighted String"
message:highlightedString
delegate:nil
cancelButtonTitle:#"Oh Yeah"
otherButtonTitles:nil];
[alert show];
Along with HighlightedString.js :
/*!
------------------------------------------------------------------------
// Search Highlighted String
------------------------------------------------------------------------
*/
var selectedText = "";
function getHighlightedString() {
var text = window.getSelection();
selectedText = text.anchorNode.textContent.substr(text.anchorOffset, text.focusOffset - text.anchorOffset);
}
// ...
function stylizeHighlightedString() {
var range = window.getSelection().getRangeAt(0);
var selectionContents = range.extractContents();
var span = document.createElement("span");
span.appendChild(selectionContents);
span.setAttribute("class","uiWebviewHighlight");
span.style.backgroundColor = "black";
span.style.color = "white";
range.insertNode(span);
}
// helper function, recursively removes the highlights in elements and their childs
function uiWebview_RemoveAllHighlightsForElement(element) {
if (element) {
if (element.nodeType == 1) {
if (element.getAttribute("class") == "uiWebviewHighlight") {
var text = element.removeChild(element.firstChild);
element.parentNode.insertBefore(text,element);
element.parentNode.removeChild(element);
return true;
} else {
var normalize = false;
for (var i=element.childNodes.length-1; i>=0; i--) {
if (uiWebview_RemoveAllHighlightsForElement(element.childNodes[i])) {
normalize = true;
}
}
if (normalize) {
element.normalize();
}
}
}
}
return false;
}
// the main entry point to remove the highlights
function uiWebview_RemoveAllHighlights() {
selectedText = "";
uiWebview_RemoveAllHighlightsForElement(document.body);
}
I always get nothing as a result ... The alert view shows nothing...What's the problem with this code ? Any help ? Any ideas ? It will be really appreciated.
The solution was actually pretty simple and no need for all the above code!
For any future users just use:
NSString *textToSpeech = [WebV2 stringByEvaluatingJavaScriptFromString: #"window.getSelection().toString()"];
NSLog(#" -**-*--****-*---**--*-* This is the new select text %#",[WebV2 stringByEvaluatingJavaScriptFromString: #"window.getSelection().toString()"] );
NSString *theSelectedText = [self.webView stringByEvaluatingJavaScriptFromString:#"window.getSelection().toString()"];
This will pass your selection to the string variable.
I would like to know if its possible to change the map overlay alpha based on selecting the maptype here is my code I thought might work but it doesn't seem to. Can anyone provide some incite?
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay{
TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
if(mapview.mapType == MKMapTypeHybrid) {
view.tileAlpha = 0.55;
} else if(mapview.mapType == MKMapTypeSatellite) {
view.tileAlpha = 0.0;
} else {
view.tileAlpha = 0.75;
}
return [view autorelease];
}
This code works fine
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKCircleView *circleView = [[MKCircleView alloc] initWithCircle:overlay];
CGFloat alpha;
if (mapView.mapType == MKMapTypeStandard) {
alpha = 0.5f;
} else {
alpha = 1.0f;
}
circleView.fillColor = [[UIColor blackColor] colorWithAlphaComponent:alpha];
return circleView;
}
But in mapView:viewForOverlay you set alpha based on current map type. To change alpha when map change map type you need to observe mapType property using KVO. So when map type is changing you just set new alpha for all overlays. To get view for overlay use
[mapView viewForOverlay:(id<MKOverlay>)overlay];
I am little slow in English, please do understand.
Here is my source code:
- (void)createBall:(CGPoint)touchedAt{
CGSize winSize = [CCDirector sharedDirector].winSize;
ball2 = [CCSprite spriteWithFile:#"Ball.png" rect:CGRectMake(0, 0, 54, 54)];
ball2.position = ccp(touchedAt.x,touchedAt.y);
[self addChild:ball2];
b2BodyDef ballBodyDef2;
ballBodyDef2.type = b2_dynamicBody;
ballBodyDef2.position.Set(touchedAt.x/PTM_RATIO, touchedAt.y/PTM_RATIO);
ballBodyDef2.userData = ball2;
b2Body *body2 = _world->CreateBody(&ballBodyDef2);
b2CircleShape circle;
circle.m_radius = 89.0/PTM_RATIO;//(arc4random()*26.0)/PTM_RATIO;
b2FixtureDef ballShapeDef2;
ballShapeDef2.shape = &circle;
ballShapeDef2.density = 1.0f;
ballShapeDef2.friction = 0.2f;
ballShapeDef2.restitution = 0.8f;
body2->CreateFixture(&ballShapeDef2);
}
-(void)createBall2
{
CGSize winSize = [CCDirector sharedDirector].winSize;
globalSprite = [CCSprite spriteWithFile:#"Ball.png"];
globalSprite.position = ccp(winSize.width/2 + globalSprite.contentSize.width, winSize.height/2);
[self addChild:globalSprite];
b2BodyDef ballBodyDef3;
ballBodyDef3.type = b2_dynamicBody;
ballBodyDef3.position.Set(100/PTM_RATIO, 100/PTM_RATIO);
ballBodyDef3.userData = globalSprite ;
b2Body *body3 = _world->CreateBody(&ballBodyDef3);
b2CircleShape circle;
circle.m_radius = 26.0/PTM_RATIO;//(arc4random()*26.0)/PTM_RATIO;
b2FixtureDef ballShapeDef3;
ballShapeDef3.shape = &circle;
ballShapeDef3.density = 1.0f;
ballShapeDef3.friction = 0.2f;
ballShapeDef3.restitution = 0.8f;
body3->CreateFixture(&ballShapeDef3);
}
// initialize your instance here
-(id) init
{
if( (self=[super init])) {
// enable touch
// enable accelerometer
CGSize winSize = [CCDirector sharedDirector].winSize;
self.isAccelerometerEnabled = YES;
self.isTouchEnabled = YES;
// Create sprite and add it to the layer
// Create a world
b2Vec2 gravity = b2Vec2(0.0f, 0.0f);
bool doSleep = true;
_world = new b2World(gravity, doSleep);
// Create edges around the entire screen
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
b2Body *groundBody = _world->CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &groundBox;
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
groundBody->CreateFixture(&boxShapeDef);
groundBox.SetAsEdge(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO));
groundBody->CreateFixture(&boxShapeDef);
groundBox.SetAsEdge(b2Vec2(0, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO));
groundBody->CreateFixture(&boxShapeDef);
groundBox.SetAsEdge(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO), b2Vec2(winSize.width/PTM_RATIO, 0));
groundBody->CreateFixture(&boxShapeDef);
// Create ball body and shape
[self schedule:#selector(tick:)];
//[self schedule:#selector(gameLogic:) interval:1.0];
[self createBall2];
}
return self;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
[self createBall:location];
}
- (void)tick:(ccTime) dt {
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *ballData = (CCSprite *)b->GetUserData();
ballData.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
ballData.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
I want to
touch -> sprite create(circle) -> sprite scale -> sprite remove
but
- (void)tick:(ccTime) dt <---------- this is simulator turn off!
I want to way
Try this :
world->DestroyBody(sprite);