protected void EnsureObjectsCreated() { ClearCreatedObjects(); Texture prototypeTexture = null; if (useTextures) { prototypeMaterial = MaterialManager.Instance.Load("barrel.barrel"); if (uniqueTextures) prototypeTexture = TextureManager.Instance.Load("blank.dds"); } else prototypeMaterial = MaterialManager.Instance.Load("unit_box.unit_box"); prototypeMaterial.Compile(); if (objectCount == 0) return; int materialCount = (animatedObjects ? 0 : (numObjectsSharingMaterial == 0 ? objectCount : (numObjectsSharingMaterial >= objectCount ? 1 : (objectCount + numObjectsSharingMaterial - 1) / numObjectsSharingMaterial))); materialCountLabel.Text = "Material Count: " + materialCount; if (whichObjects == WhichObjectsEnum.woPlane || whichObjects == WhichObjectsEnum.woRandom) { Mesh plane = meshes[(int)WhichObjectsEnum.woPlane]; if (plane != null) plane.Unload(); // Create the plane float planeSide = 1000f; int planeUnits = Int32.Parse(planeUnitsTextBox.Text); plane = MeshManager.Instance.CreatePlane("testerPlane", new Plane(Vector3.UnitZ, Vector3.Zero), planeSide, planeSide, planeUnits, planeUnits, true, 1, planeSide / planeUnits, planeSide / planeUnits, Vector3.UnitY); meshes[(int)WhichObjectsEnum.woPlane] = plane; } // Create the new materials for (int i = 0; i < materialCount; i++) { Material mat = prototypeMaterial.Clone("mat" + i); Pass p = mat.GetTechnique(0).GetPass(0); if (!animatedObjects && uniqueTextures) { Texture t = prototypeTexture; Texture texture = TextureManager.Instance.CreateManual("texture" + i, t.TextureType, t.Width, t.Height, t.NumMipMaps, t.Format, t.Usage); textureList.Add(texture); p.CreateTextureUnitState(texture.Name); } // Make the materials lovely shades of blue p.Ambient = new ColorEx(1f, .2f, .2f, (1f / materialCount) * i); p.Diffuse = new ColorEx(p.Ambient); p.Specular = new ColorEx(1f, 0f, 0f, 0f); materialList.Add(mat); } // Create the entities and scene nodes for (int i=0; i<objectCount; i++) { Mesh mesh = selectMesh(); Material mat = null; if (materialCount > 0) mat = materialList[i % materialCount]; Entity entity = scene.CreateEntity("entity" + i, mesh); if (animatedObjects) { string[] visibleSubs = visibleSubMeshes[(int)whichObjects - (int) WhichObjectsEnum.woZombie]; for (int j = 0; j < entity.SubEntityCount; ++j) { SubEntity sub = entity.GetSubEntity(j); bool visible = false; foreach (string s in visibleSubs) { if (s == sub.SubMesh.Name) { visible = true; break; } } sub.IsVisible = visible; if (visible) totalVertexCount += sub.SubMesh.VertexData.vertexCount; } } else { if (mesh.SharedVertexData != null) totalVertexCount += mesh.SharedVertexData.vertexCount; else { for (int j=0; j<mesh.SubMeshCount; j++) { SubMesh subMesh = mesh.GetSubMesh(j); totalVertexCount += subMesh.VertexData.vertexCount; } } } if (animatedObjects && animateCheckBox.Checked) { AnimationState currentAnimation = entity.GetAnimationState(GetAnimationName()); currentAnimation.IsEnabled = true; if (!animationInitialized) { currentAnimationLength = entity.GetAnimationState(GetAnimationName()).Length; animationInitialized = true; } } if (mat != null) entity.MaterialName = mat.Name; entityList.Add(entity); SceneNode node = scene.RootSceneNode.CreateChildSceneNode(); sceneNodeList.Add(node); node.AttachObject(entity); node.Position = new Vector3(randomCoord(), randomCoord(), randomCoord()); if (randomSizes) node.ScaleFactor = Vector3.UnitScale * randomScale(); else if (randomScales) node.ScaleFactor = new Vector3(randomScale(), randomScale(), randomScale()); else node.ScaleFactor = Vector3.UnitScale * 1f; if (randomOrientations) { Vector3 axis = new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()); node.Orientation = Vector3.UnitY.GetRotationTo(axis.ToNormalized()); } else node.Orientation = Quaternion.Identity; } }
// Decide, based on the normal to the collision object, if the // moving object can slide across the obstacle, and if it can, // return the updated displacement. This displacement may in fact // run into _another_ obstacle, however, so the call must again // run the collision test. private static bool DecideToSlide(MovingObject mo, Vector3 mobNodePosition, CollisionParms parms, ref Vector3 displacement) { Vector3 normDisplacement = displacement.ToNormalized(); Vector3 normObstacle = parms.normObstacle.ToNormalized(); if (MO.DoLog) { MO.Log(" DecideToSlide: normObstacle {0}, normDisplacement {1}", normObstacle.ToString(), normDisplacement.ToString()); MO.Log(" DecideToSlide: displacement {0}", displacement); } // First we find the angle between the normal and the // direction of travel, and reject the displacement if // it's too small float slideAngle = (NormalizeAngle((float)Math.Acos((double)normDisplacement.Dot(normObstacle))) - .5f * (float)Math.PI); if (Math.Abs(slideAngle) > CollisionAPI.MinSlideAngle) { if (MO.DoLog) MO.Log(" After collision, displacement {0}, won't slide because slideAngle {1} > minSlideAngle {2}", displacement.ToString(), slideAngle, CollisionAPI.MinSlideAngle); displacement = Vector3.Zero; return false; } // Then we find the angle with the y axis, and reject the // displacement if it's too steep float verticalAngle = NormalizeAngle((float)Math.Acos((double)normDisplacement[1])); if (Math.Abs(verticalAngle) > CollisionAPI.MaxVerticalAngle) { if (MO.DoLog) MO.Log(" After collision, displacement {0}, won't slide because verticalAngle {1} <= maxVerticalAngle {2}", displacement.ToString(), verticalAngle, CollisionAPI.MaxVerticalAngle); displacement = Vector3.Zero; return false; } // Else, we can slide, so return a displacement that // points in the direction we're sliding, and has length // equal to a constant times the displacement length // Rotate displacement so that it's 90 degress from the // obstacle normal Vector3 cross = normObstacle.Cross(normDisplacement); Quaternion q = Quaternion.FromAngleAxis(.5f * (float)Math.PI, cross); Matrix4 transform = q.ToRotationMatrix(); Vector3 transformedNorm = transform * normObstacle.ToNormalized(); float len = displacement.Length; displacement = transformedNorm * len; // Vector3 alignedPart = normObstacle * (normObstacle.Dot(displacement)); // displacement -= alignedPart; Vector3 p = mobNodePosition + displacement; float h = worldManager.GetHeightAt(p); // If sliding would put us below ground, limit the displacement if (h > p.y) { if (MO.DoLog) MO.Log(" Sliding up because terrain height is {0} is higher than projected mobNode height {1}", h, p.y); displacement.y += h - p.y; } if (MO.DoLog) { MO.Log(" Exiting DecideToSlide, sliding displacement {0}, slideAngle {1}, verticalAngle {2}", displacement.ToString(), slideAngle, verticalAngle); MO.Log(" Exiting DecideToSlide, cross product {0}, quaternion {1}, transformedNorm {2}", cross, q, transformedNorm); // MO.Log(" Exiting DecideToSlide, alignedPart {0}", alignedPart); } return true; }
/// <summary> /// This method transforms a Node by a weighted amount from its /// initial state. If weighted transforms have already been applied, /// the previous transforms and this one are blended together based /// on their relative weight. This method should not be used in /// combination with the unweighted rotate, translate etc methods. /// </summary> /// <param name="weight"></param> /// <param name="translate"></param> /// <param name="rotate"></param> /// <param name="scale"></param> internal virtual void WeightedTransform(float weight, ref Vector3 translate, ref Quaternion rotate, ref Vector3 scale, bool lookInMovementDirection) { // If no previous transforms, we can just apply if (accumAnimWeight == 0.0f) { rotationFromInitial = rotate; translationFromInitial = translate; scaleFromInitial = scale; accumAnimWeight = weight; } else { // Blend with existing float factor = weight / (accumAnimWeight + weight); // translationFromInitial += (translate - translationFromInitial) * factor; Vector3 tmp = Vector3.Zero; translationFromInitial.x += (translate.x - translationFromInitial.x) * factor; translationFromInitial.y += (translate.y - translationFromInitial.y) * factor; translationFromInitial.z += (translate.z - translationFromInitial.z) * factor; Quaternion result = Quaternion.Zero; Quaternion.SlerpRef(ref result, factor, ref rotationFromInitial, ref rotate, false); rotationFromInitial = result; // For scale, find delta from 1.0, factor then add back before applying scaleFromInitial.x *= 1.0f + (scale.x - 1.0f) * factor; scaleFromInitial.y *= 1.0f + (scale.y - 1.0f) * factor; scaleFromInitial.z *= 1.0f + (scale.z - 1.0f) * factor; accumAnimWeight += weight; } // Update final based on bind position + offsets // orientation = initialOrientation * rotationFromInitial; Quaternion.MultiplyRef(ref orientation, ref initialOrientation, ref rotationFromInitial); // position = initialPosition + translationFromInitial; position.x = initialPosition.x + translationFromInitial.x; position.y = initialPosition.y + translationFromInitial.y; position.z = initialPosition.z + translationFromInitial.z; // scale = initialScale * scaleFromInitial; scale.x = initialScale.x * scaleFromInitial.x; scale.y = initialScale.y * scaleFromInitial.y; scale.z = initialScale.z * scaleFromInitial.z; if(lookInMovementDirection) orientation = -Vector3.UnitX.GetRotationTo(translate.ToNormalized()); NeedUpdate(); }