Tag: Objective-C


iPhone SDK Mesh COLLADA importieren

19 July 2009 — 17:46

Software:    Maya 2008
COLLADA version 1.4
COLLADA Schema unterstützt alle Features, die modern 3D interactive applications brauchen, einschließen Shader Effekten,  Animation und Physik Simulation.
Step 1.
Installieren und laden COLLADA plug in für Maya
Step 2.
Wechseln alle Face vom Model zu Dreiecken: Mesh -> Triangulate. (OpenGL ES 1.x do not support QUADE draw).
Step 3.
Export Model mit mayaCollada, dann habe ich a dae(digital asset exchange) file.  Eigentlich dea file ist erweitern aus XML File.
Step 4.
Gehen wir zurück zu Xcode und import diesen .dae File into project, öffnen es mit Editor, wir können alle informationen über 3D Model, wie Materialien, Effekte, Geometrie… Ich konzentriere mich auf tag “library geomentry” welche ein Kinder Tag “mesh” hat. Der erste Zeile in diesem Block definiert den Name der Geometrie <geometry id=”pCubeShape1″ name=”pCubeShape1″> dann kommt das wichtige Tag <mesh>, darin gibt es 3 “source” und ein “triangles” Blocks, jede hat ein Array : <float_array id=”pCubeShape1-positions-array” count=”24″> (Position der jede vertex), <float_array id=”pCubeShape1-normals-array” count=”72″>, <source id=”pCubeShape1-map1″ name=”pCubeShape1-map1″>  (texture coordinats) and <p> ( bitte sehen Sie Ps). Das ist alles was wir brauchen ein den einfache Mordel zu importieren.
Step 5.
Danach kann ich analysieren das File mit class NSXMLParser vom SDK. In diesem Beispiel male ich Model Figur ohne Texture, deswegen lese ich nur Positions array und die indices array aus <p> aus.

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if(qName) {
elementName = qName;
}

if([elementName isEqualToString:@"accessor"]) {
NSString *accessorAtt = [attributeDict valueForKey:@"source"];
if([accessorAtt isEqualToString:@"#pCubeShape1-positions-array"]) {
NSString *sVertextCount = [attributeDict valueForKey:@"count"];
vertexCount = [sVertextCount intValue];
return;
}

else if([accessorAtt isEqualToString:@"#pCubeShape1-map1-array"]) {
NSString *sMapCount = [attributeDict valueForKey:@"count"];
mapCount = [sMapCount intValue];
return;
}
}

else if([elementName isEqualToString:@"triangles"]) {
NSString *sFaceCount = [attributeDict valueForKey:@"count"];
faceCount = [sFaceCount intValue];
return;
}

else if ([elementName isEqualToString:@"float_array"]) {
NSString *relAtt = [attributeDict valueForKey:@"id"];
if([relAtt isEqualToString:@"pCubeShape1-positions-array"]) {
contentProperty = [NSMutableString string];
array_id = id_positions;
NSLog(@”get positions”);

}

else if([relAtt isEqualToString:@"pCubeShape1-normals-array"]) {
contentProperty = [NSMutableString string];
array_id = id_normals;
NSLog(@”get normals”);
}

else if([relAtt isEqualToString:@"pCubeShape1-map1-array"]) {
contentProperty = [NSMutableString string];
array_id = id_maps;
NSLog(@”get maps”);
}
}

else if ([elementName isEqualToString:@"p"]) {
contentProperty = [NSMutableString string];
NSLog(@”get vertex”);
}

else {
contentProperty = nil;
}
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if (qName) {
elementName = qName;
}

if ([elementName isEqualToString:@"float_array"]) {
if(array_id == id_positions) {
msd.sPosition = contentProperty;
NSLog(@”set postion”);
array_id = -1;
}

if(array_id == id_normals) {
msd.sNormal = contentProperty;
NSLog(@”set normals”);
array_id = -1;
}

if(array_id == id_maps) {
msd.sMap = contentProperty;
NSLog(@”set maps”);
array_id = -1;
}
}

else if ([elementName isEqualToString:@"p"]) {
msd.sIndices = contentProperty;
NSLog(@”set vertex”);
}

}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (contentProperty) {
[contentProperty appendString:string];
}
}

Step 6.
Zum Schluss malen wir Model mit OpenGL ES.

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, mVertices);

glDrawArrays(GL_TRIANGLES, 0, faceCount*3);

glDisableClientState(GL_VERTEX_ARRAY);

Ps:
<p> beinhaltet Indices, die die Vertexattribute einer Anzahl von Dreiecken beschreiben. Die Indices in einem <p>-Element beziehen sich auf verschiedene Eingaben abhaengig von ihrer Ordnung. Der erste Index in einem <p>-Element bezieht sich auf alle Eingaben mit dem Offset 0. Der zweite Index bezieht sich auf alle Eingaben mit dem Offset 1.
Jede Vertex/Ecke der Dreiecke wird …. Nachdem jede Eingabe benutzt wurde, bezieht sich der naechste Index wieder auf die Eingabe mit Offset 0 und beginnt eine neue Vertex/Ecke.
Die Windungsreihenfolge der produzierten Vertices/Ecken ist entgegen dem Uhrzeigersinn und beschreibt die Vorderseite des jeweiligen Dreiecks.
Wenn die primitives ohne Eckennormalen zusammengestellt sind, dann kann die Anwendung per-primitive …. generieren……

Hier ist ein Beispiel eines <triangle>-Elements dass zwei Dreiecke beschreibt. Es gibt zwei <source>-Elemente, die die Position und die Normalen-Daten enthalten, entsprechend den <input>-Element Bedeutungen. Die <p>-Element Indexwerte geben die Reihenfolge in der die Eingabewerte genutzt werden an:
<mesh>
<source id=”position”/>
<source id=”normal”/>
<vertices id=”verts”>
<input semantic=”POSITION” source=”#position”/>
</vertices>
<triangles count=”2″ material=”Bricks”>
<input semantic=”VERTEX” source=”#verts” offset=”0″/>
<input semantic=”NORMAL” source=”#normal” offset=”1″/>
<p>
0 0 1 3 2 1
0 0 2 1 3 2
</p>
</triangles>
</mesh>

3 Kommentare » | Allgemein

Properties für Accessoren

3 June 2009 — 10:32

Datenkapselung ist einer der wichtigsten Konzepte in der objektorientierten Programmierung. Um Datenkapselung zu gewährleisten, müssen Setter- und Getter-Methoden (Accessoren) für jede Instanzvariable geschrieben werden. Dies kann unter Umständen sehr zeitaufwendig sein. Die meisten Accessoren haben den gleichen Aufbau und unterscheiden sich lediglich durch die Instanzvariable. Da liegt die Idee nahe, diese Accessoren automatisch generieren zu lassen. Dieses vorgehen nennt man synthetisieren und wurde in Objektive-C 2.0 mit den sogenannten Properties eingeführt.

Propertys haben die allgemeine Syntax:

header
@property (attribut1, attribut2, attribut3, attribut4) datentyp variablenname;

implementation
@syntesize variablenname;

Hat man eine Instanzvariable synthetisiert kann wie folgt darauf zugegriffen werden:

Standard-Syntax
[Obj setVariablenname:wert]
printf(„Der wert lautet %i“, [Obj variablenname]);

Punkt-Syntax
Obj.variblenname = wert;
printf(„Der wert lautet %i“, Obj.variablenname);

Anmerkung:
Die Punkt-Syntax ist unabhängig von den Properties. Es ist möglich die Accessoren selbst zu schreiben (ohne Properties) und über die Punkt Syntax darauf zuzugreifen.

Die 4 Attribute beschreiben folgendes:
-Zugriffsart (readonly, readwrite)
-Setter-Semantik (assign, retain, copy
-Atomicity
-Setter- und Getter-Namen

Zugriffsart

Es gibt 2 Zugriffsarten. Entweder nur lesen (readonly) oder schreiben und lesen (readwrite).
Bei readwrite werden beide Accessoren synthetisiert (vom compiler geschrieben). Im Falle von read wird nur der Getter synthetisiert und somit ist nur ein lesender Zugriff möglich.

Setter-Semantik

Assign

Assign ist die standard Setter-Semantik. Falls man also nichts angibt wird vom Compiler   automatisch assign genommen. Der neue Wert wird der Instanzvariable einfach nur zugewiesen, sonst nichts. Der Referenzcounter wird nicht erhöht. Dadurch ist eine ältere Erhöhung des Referenzcounters ausgeschlossen und er wird somit auch nicht erniedrigt. Dies hat zur Folge, dass die Empfänger -Klasse kein Besitzer des Objektes ist. Wird der Referenzcounter des Objekts von der Sender-Klasse erniedrigt, wird das Objekt gelöscht und ist somit auch nicht mehr für die Empfänger-Klasse erreichbar. Natürlich ist es auch umgekehrt möglich. Deswegen sollte man Propertys mit der assign-Semantik in der dealloc-Methode nicht releasen (den es wurde auch nirgends retained).

Beispiel (vom Compiler generierter Code für den Setter)
-(void)setVariablenname:(id)senderValue
{
   instanceValue = senderValue;
}

 assign

Retain

Retain löst die Problematik der Assign-Semantik. Die Instanzvariable wird zunächst released, da es vielleicht schon auf ein Objekt zeigt. Danach wird der neue Wert zugewiesen und dann wird die Instanzvariable retained. Dies bedeutet nun dass die Empfänger-Klasse ein Besitzer des Objektes ist. Die Sender-Klasse kann das Objekt nun löschen und es ist für die Empfänger-Klasse immernoch  erreichbar. Da man in der Retain-Semantik die Instanzvariable retained, muss sie in der dealloc-Methode auch released werden. Beide Klassen (Sender und Empfänger) zeigen nun auf dasselbe Objekt. Ändert eine Klasse das Objekt ist es auch für die andere geändert.

Beispiel  (vom Compiler generierter Code für den Setter)

-(void)setVariablenname:(id)senderValue
if(instanceValue != senderValue)
  {
    [instanceValue release];
    instanceValue = senderValue;
   [instanceValue retain];
}
}

retain

Copy

Copy erlaubt es einer Klasse der alleinige Beitzer eines Objektes zu werden. Das übergebene Objekt wird kopiert und danach erst zugewiesen. Dies hat den Vorteil dass Empfänger- und Sender-Klasse nicht mehr auf dasselbe Objekt zeigen und somit eine Änderung des Objekts der einen Klasse nicht für das Objekt der Anderen gilt. Die Klasse des übergebenen Objekts muss das NSCopying Protocol implementieren.

Beispiel (vom Compiler generierter Code für den Setter)
-(void)setVariablenname:(id)senderValue
{
if(instanceValue != senderValue)
  {
    [instanceValue release];
    instanceValue = [senderValue copy];
}
}

copy

Atomicity

Atomicity gewährleistet den sicheren Zugriff von mehreren Threads auf die Accessoren. Zu einer bestimmten Zeit darf nur ein Thread auf die Accessoren  zugreifen. Möchte man dies vermeiden gibt man einfach das  Attribut „nonatomic“ an. Mehr dazu auf friday.com

Setter- und Getter-Namen

Wie weiter oben beschrieben sind die Standard-Namen: „setVariablenname:“ (Setter) und „variablenname“ (Getter). Dies kann geändert werden. Um zum Beispiel im Falle von Boolean-Werten für den Getter einen Namen wie „isInitial“ zu erzeugen, kann man als Attribut „getter = isInitial“ angeben. Dadurch kann man auf den Boolwert wie folgt zugreifen:

[Object isInitial];
Object.isInitial;

Natürlich kann auch der Settername durch „setter=andererName“ geändert werden.
Außer man hat als Zugriffsart „read“ definiert, dann gibt es nähmlich einen Compilerfehler, da der Setter nicht existiert.

Quelle
developer.apple.com

5 Kommentare » | ObjC

Nachrichten VS Methodenaufrufe

12 May 2009 — 16:22

Um den Umstieg von C++ oder Java auf ObjC (Objective-C) zu erleichtern, poste ich hier den meiner Meinung nach wichtigsten Unterschied.

Einer der Hauptunterschiede von ObjC (Objective-C) zu C++ und Java liegt in der Philosophie hinter dem Aufruf von Instanzfunktionen. Während man in C++ oder Java von einem Methodenaufruf eines Objektes spricht, ist es unter ObjC das verschicken einer Nachricht an das Objekt.

Bei einem Methodenaufruf unter C++ oder Java wird bereits vor dem Ausführen des Programms die Methode an das Objekt gebunden. Das heißt, die Methode des Objektes muss existieren, ansonsten gibt es einen Compilerfehler und ein Start des Programms ist unmöglich. Dieses frühe Binden hat den Vorteil fehlerhafte Methodenaufrufe zu vermeiden, ist jedoch sehr unflexibel.

Unter ObjC verschickt man Nachrichten an Objekte, was übrigens von Smalltalk stammt. Diese Nachrichten werden erst zur Laufzeit an das Objekt geschickt. Dadurch ist eine späte Bindung möglich. Die Nachrichten werden zur Laufzeit vom Objekt interpretiert und falls eine passende Funktion vorhanden ist, wird diese ausgeführt. Dies bedeutet dass der Compiler vor dem Start nicht überprüft ob das Objekt die aufgerufene Funktion besitzt. Spätes binden eines Funktionsaufrufs hat den Nachteil dass es fehlerhafte Funktionsaufrufe geben kann, jedoch den entscheidenden Vorteil der Flexibilität.

Dieses Beispiel sieht sehr simpel aus würde jedoch mit C++ oder Java nicht laufen:

implementation ClassOne

-(void)doSomeThing
{…}
@end

implementation ClassTwo

-(void)doSomeThing
{…}
@end

int main(…)
{
id *object = [[ClassOne alloc]init]; //id ist Typlos und steht für alle Typen von Objekten
[object doSomeThing]; //ruft die Methode von ClassOne
[object release];
object = [[ClassTwo alloc]init];
[object doSomeThing]; //ruft die Methode von ClassTwo
}

1 Kommentar » | ObjC

Zurück nach oben